#!/usr/bin/env perl
#-----------------------------------------------------------------------------------------------
#
# build-namelist
#
# This script builds the namelists for the standalone CAM configuration of CCSM4.
# Eventually the system's build will be reworked so that individual components are responsible
# for building their own namelists.
#
# build-namelist is designed to be used in conjuction with configure.  By default configure
# produces a config_cache.xml file that contains all information needed at build time to procduce
# a CAM executable.  build-namelist reads this file to obtain information it needs to provide
# default values that are consistent with the CAM executable.  For example, the grid resolution
# is obtained from the cache file and used to determine appropriate defaults for boundary datasets
# that are resolution dependent.
#
# The simplest use of build-namelist is to execute it from the build directory where configure
# was run.  By default it will use the config_cache.xml file that was written by configure to
# determine the build time properties of the executable, and will write the files that contain 
# the output namelists in that same directory.  But if multiple runs are to made using the
# same executable, successive invocations of build-namelist will overwrite previously generated
# namelist files.  So generally the best strategy is to invoke build-namelist from the run
# directory and use the -config option to provide the filepath of the config_cache.xml file.
#
#
# Date        Contributor      Modification
# -------------------------------------------------------------------------------------------
# 2007-12-31  Brian Eaton      Original version
# 2008-02-02  B. Eaton         Restore -test functionality.
# 2008-07-01  Sean Santos      Added -inputdata functionality.
# 2008-07-09  B. Eaton         Provide default values for rad_climate variable which
#                              specifies the radiatively active constituents.
# 2008-08-26  B. Eaton         Add the driver namelist group ccsm_pes to specify the
#                              task/thread layout for all components.
# 2008-11-14  B. Eaton         Extend use_case functionality.
# 2009-09-02  B. Eaton         Allow namelist definition, defaults, and use case files
#                              to come from the user source mod directories.
#--------------------------------------------------------------------------------------------

use strict;
#use warnings;
#use diagnostics;

use Cwd;
use English;
use File::Basename;
use Getopt::Long;
use IO::File;
use FindBin qw($Bin);
use lib "$Bin/perl5lib";
use Build::ChemNamelist qw(set_dep_lists);
#-----------------------------------------------------------------------------------------------

sub usage {
    die <<EOF;
SYNOPSIS
     build-namelist [options]
OPTIONS
     -case "name"          Case identifier up to 32 characters
     -cice_nl "namelist"   Specify namelist settings for CICE directly on the commandline by supplying 
                           a string containing FORTRAN namelist syntax, e.g.,
                              -cice_nl "&ice histfreq=1 /"
                           This namelist will be passed to the invocation of the CICE build-namelist
                           via its -namelist argument.
     -config "filepath"    Read the given configuration cache file to determine the configuration
                           of the CAM executable.  Default: "config_cache.xml".
     -config_cice "filepath"
                           Filepath of the CICE config_cache file.  This filepath is passed to the
                           invocation of the CICE build-namelist.  Only specify this to override the
                           default filepath which was set when the CICE configure was invoked by
                           the CAM configure.
     -csmdata "dir"        Root directory of CCSM input data.
                           Can also be set by using the CSMDATA environment variable.
     -dir "directory"      Directory where output namelist files for each component will be
                           written, i.e., atm_in, drv_in, ice_in, lnd_in and ocn_in.
                           Default: current working directory.
     -help [or -h]         Print usage to STDOUT.
     -ignore_ic_date       Ignore the date of the initial condition files
                           when determining the default.
     -ignore_ic_year       Ignore just the year part of the date of the initial condition files
                           when determining the default.
     -infile "filepath"    Specify a file containing namelists to read values from.
     -inputdata "filepath" Writes out a list containing pathnames for required input datasets in
                           file specified.
     -namelist "namelist"  Specify namelist settings directly on the commandline by supplying 
                           a string containing FORTRAN namelist syntax, e.g.,
                              -namelist "&camexp stop_option='ndays' stop_n=10 /"
     -ntasks <n>           Specify the number of MPI tasks being used by the run.  This is used
                           to set a default decomposition for the FV dycore only (npr_yz).
     -runtype "type"       Type of simulation (startup, continue, or branch)
     -silent [-s]          Turns on silent mode - only fatal messages issued.
     -test                 Enable checking that input datasets exist on local filesystem.
     -use_case             Specify a use case.  Default: present day climatology
     -verbose [or -v]      Turn on verbose echoing of informational messages.
     -version              Echo the CVS tag name used to check out this CAM distribution.

Note: The precedence for setting the values of namelist variables is (highest to lowest):
      1. namelist values set by specific command-line options, i.e., -case, 
         -runtype
      2. values set on the command-line using the -namelist option,
      3. values read from the file specified by -infile,
      4. values specified by the -use_case option,
      5. values from the namelist defaults file.
EOF
}

#-----------------------------------------------------------------------------------------------
# Set the directory that contains the CAM configuration scripts.  If the command was
# issued using a relative or absolute path, that path is in $ProgDir.  Otherwise assume the
# command was issued from the current working directory.

(my $ProgName = $0) =~ s!(.*)/!!;      # name of this script
$ProgName = "CAM $ProgName";           # Since multiple components are now using a build-namelist
                                       # utility add "CAM" qualifier to the name.  This helps when
                                       # looking at error output from the whole CCSM system.
my $ProgDir = $1;                      # name of directory containing this script -- may be a
                                       # relative or absolute path, or null if the script is in
                                       # the user's PATH
my $cwd = getcwd();                    # current working directory
my $cfgdir;                            # absolute pathname of directory that contains this script
if ($ProgDir) { 
    $cfgdir = absolute_path($ProgDir);
} else {
    $cfgdir = $cwd;
}

# CAM root directory.  
my $cam_root = absolute_path("$cfgdir/../../../..");

my $cfg_cache = "config_cache.xml";           # Default name of configuration cache file
my $outdirname = ".";                         # Default name of output directory name

#-----------------------------------------------------------------------------------------------
# Save commandline
my $commandline = "$cfgdir/build-namelist @ARGV";

#-----------------------------------------------------------------------------------------------

# Process command-line options.

my %opts = ( config         => $cfg_cache,
	     csmdata        => undef,
	     help           => 0,
	     dir            => $outdirname,
	     silent         => 0,
	     test           => 0,
	    );

GetOptions(
    "case=s"                    => \$opts{'case'},
    "cice_nl=s"                 => \$opts{'cice_nl'},
    "config=s"                  => \$opts{'config'},
    "config_cice=s"             => \$opts{'config_cice'},
    "csmdata=s"                 => \$opts{'csmdata'},
    "d|dir=s"                   => \$opts{'dir'},
    "h|help"                    => \$opts{'help'},
    "ignore_ic_date"            => \$opts{'ignore_ic_date'},
    "ignore_ic_year"            => \$opts{'ignore_ic_year'},
    "infile=s"                  => \$opts{'infile'},
    "inputdata=s"               => \$opts{'inputdata'},
    "namelist=s"                => \$opts{'namelist'},
    "ntasks=s"                  => \$opts{'ntasks'},
    "runtype=s"                 => \$opts{'runtype'},
    "s|silent"                  => \$opts{'silent'},
    "test"                      => \$opts{'test'},
    "uc|use_case=s"             => \$opts{'use_case'},
    "v|verbose"                 => \$opts{'verbose'},
    "version"                   => \$opts{'version'},
)  or usage();

# Give usage message.
usage() if $opts{'help'};

# Echo version info.
if ($opts{'version'}) {
    version($cfgdir);
    exit;
}

# Check for unparsed arguments
if (@ARGV) {
    print "ERROR: unrecognized arguments: @ARGV\n";
    usage();
}

# Define print levels:
# 0 - only issue fatal error messages
# 1 - only informs what files are created (default)
# 2 - verbose
my $print = 1;
if ($opts{'silent'})  { $print = 0; }
if ($opts{'verbose'}) { $print = 2; }
my $eol = "\n";

if ($print>=2) { print "Setting CAM configuration script directory to $cfgdir$eol"; }
if ($print>=2) { print "build-namelist was invoked with the commandline:\n $commandline$eol"; }

# Check that configuration cache file exists.
(-f $opts{'config'})  or  die <<"EOF";
** $ProgName - ERROR: Cannot find configuration cache file: \"$opts{'config'}\" **
EOF

if ($print>=2) { print "Using CAM configuration cache file $opts{'config'}$eol"; }

# Check that the CCSM inputdata root directory has been specified.
my $inputdata_rootdir = undef;
if (defined($opts{'csmdata'})) {
    $inputdata_rootdir = $opts{'csmdata'};
}
elsif (defined $ENV{'CSMDATA'}) {
    $inputdata_rootdir = $ENV{'CSMDATA'};
}
else {
    die "$ProgName - ERROR: CCSM inputdata root directory must be specified by either -csmdata argument\n" .
	" or by the CSMDATA environment variable. :";
}

if ($print>=2) { print "CCSM inputdata root directory: $inputdata_rootdir$eol"; }

# If the -test option is specified, then the inputdata root directory must be local or nfs mounted.
if ($opts{'test'}) {
(-d $inputdata_rootdir)  or  die <<"EOF";
** $ProgName - ERROR: CCSM inputdata root is not a directory: \"$inputdata_rootdir\" **
EOF
}

#-----------------------------------------------------------------------------------------------
# Make sure we can find required perl modules, definition, and defaults files.
# Look for them under the directory that contains the configure script.

# The XML::Lite module is required to parse the XML files.
(-f "$cfgdir/perl5lib/XML/Lite.pm")  or  die <<"EOF";
** $ProgName - ERROR: Cannot find perl module \"XML/Lite.pm\" in directory \"$cfgdir/perl5lib\" **
EOF

# The Build::Config module provides utilities to access the configuration information
# in the config_cache.xml file
(-f "$cfgdir/perl5lib/Build/Config.pm")  or  die <<"EOF";
** $ProgName - ERROR: Cannot find perl module \"Build/Config.pm\" in directory \"$cfgdir/perl5lib\" **
EOF

# The Build::NamelistDefinition module provides utilities to validate that the output
# namelists are consistent with the namelist definition file
(-f "$cfgdir/perl5lib/Build/NamelistDefinition.pm")  or  die <<"EOF";
** $ProgName - ERROR: Cannot find perl module \"Build/NamelistDefinition.pm\" in directory \"$cfgdir/perl5lib\" **
EOF

# The Build::NamelistDefaults module provides a utility to obtain default values of namelist
# variables based on finding a best fit with the attributes specified in the defaults file.
(-f "$cfgdir/perl5lib/Build/NamelistDefaults.pm")  or  die <<"EOF";
** $ProgName - ERROR: Cannot find perl module \"Build/NamelistDefaults.pm\" in directory \"$cfgdir/perl5lib\" **
EOF

# The Build::Namelist module provides utilities to parse input namelists, to query and modify
# namelists, and to write output namelists.
(-f "$cfgdir/perl5lib/Build/Namelist.pm")  or  die <<"EOF";
** $ProgName - ERROR: Cannot find perl module \"Build/Namelist.pm\" in directory \"$cfgdir/perl5lib\" **
EOF

#-----------------------------------------------------------------------------------------------
# Add $cfgdir/perl5lib to the list of paths that Perl searches for modules
unshift @INC, "$cfgdir/perl5lib";
require XML::Lite;
require Build::Config;
require Build::NamelistDefinition;
require Build::NamelistDefaults;
require Build::Namelist;

#-----------------------------------------------------------------------------------------------
# Create a configuration object from CAM's config_cache.xml file.  This object contains
# all the build-time specifications of the CAM executable.
my $cfg = Build::Config->new($opts{'config'});

#-----------------------------------------------------------------------------------------------
# Create a namelist definition object.  This object provides a method for verifying that the
# output namelist variables are in the definition file, and are output in the correct
# namelist groups.  Requires a namelist definition file...
#
# The namelist definition file contains entries for all namelist variables that
# can be output by build-namelist.  The version of the file that is associate with a
# fixed CAM tag is $cfgdir/namelist_files/namelist_definition.xml.  To aid developers
# who make use of the source mods directory (via -usr_src arg to configure) we allow
# the definition file to come from one of those directories.

my $nl_definition_file;
my @usr_src_dirs = split ',', $cfg->get('usr_src');
if (@usr_src_dirs) {
    foreach my $dir (@usr_src_dirs) {
	if (-f "$dir/namelist_definition.xml") {
	    $nl_definition_file = "$dir/namelist_definition.xml";
	    last;
	}
    }
}
if (! defined $nl_definition_file) {
    # default location of namelist definition file
    $nl_definition_file = "$cfgdir/namelist_files/namelist_definition.xml";
    (-f "$nl_definition_file")  or  die <<"EOF";
    ** $ProgName - ERROR: Cannot find namelist definition file \"$nl_definition_file\" **
EOF
}

if ($print>=2) { print "Using namelist definition file $nl_definition_file$eol"; }

my $definition = Build::NamelistDefinition->new($nl_definition_file);

#-----------------------------------------------------------------------------------------------
# Create a namelist defaults object.  This object provides default values for variables
# contained in the input defaults file.  The configuration object provides attribute
# values that are relevent for the CAM executable for which the namelist is being produced.
# These attributes are used along with optional user specified attributes to find the
# best match when looking for default values.
#
# The namelist defaults file contains default values for all required namelist variables.
# Analogously to the definition file, we allow a user modified version of this file to
# be present in one of the usr_src directories.

my $nl_defaults_file;
if (@usr_src_dirs) {
    foreach my $dir (@usr_src_dirs) {
	if (-f "$dir/namelist_defaults_cam.xml") {
	    $nl_defaults_file = "$dir/namelist_defaults_cam.xml";
	    last;
	}
    }
}
if (! defined $nl_defaults_file) {
    # default location of namelist defaults file
    $nl_defaults_file = "$cfgdir/namelist_files/namelist_defaults_cam.xml";
    (-f "$nl_defaults_file")  or  die <<"EOF";
    ** $ProgName - ERROR: Cannot find namelist defaults file \"$nl_defaults_file\" **
EOF
}

if ($print>=2) { print "Using namelist defaults file $nl_defaults_file$eol"; }

my $defaults = Build::NamelistDefaults->new($nl_defaults_file, $cfg);

#-----------------------------------------------------------------------------------------------
# Similarly to the namelist definition and defaults files, the use case file
# may also come from the usr_src directories.  Determine the location of the 
# use case file.

my $use_case_file;
if (defined $opts{'use_case'}) {

    if (@usr_src_dirs) {
	foreach my $dir (@usr_src_dirs) {
	    if (-f "$dir/$opts{'use_case'}.xml") {
		$use_case_file = "$dir/$opts{'use_case'}.xml";
		last;
	    }
	}
    }

    if (! defined $use_case_file) {
	# default location of namelist use case files
	$opts{'use_case_dir'} = "$cfgdir/namelist_files/use_cases";
	validate_use_case("commandline", \%opts);
	$use_case_file = "$opts{'use_case_dir'}/$opts{'use_case'}.xml";
    }

    if ($print>=2) { print "Using namelist use case file $use_case_file$eol"; }
}

#-----------------------------------------------------------------------------------------------
# Create an empty namelist object.  Add values to it in order of precedence.
my $nl = Build::Namelist->new();

#-----------------------------------------------------------------------------------------------

# Some regular expressions...
# **N.B.** the use of qr// for precompiling regexps isn't supported until perl 5.005.
my $TRUE  = "\.true\.";
my $FALSE = "\.false\.";

#-----------------------------------------------------------------------------------------------

# Process the user input in order of precedence.  At each point we'll only add new
# values to the namelist and not overwrite previously specified specified values which
# have higher precedence.

# Process the commandline args that provide specific namelist values.

# Case name
if (defined $opts{'case'}) { add_default($nl, 'case_name', 'val'=>$opts{'case'}); }

# Run type
if (defined $opts{'runtype'}) { add_default($nl, 'start_type', 'val'=>$opts{'runtype'}); }

# Process the -namelist arg.

if (defined $opts{'namelist'}) {
    # Parse commandline namelist
    my $nl_arg = Build::Namelist->new($opts{'namelist'});

    # Validate input namelist -- trap exceptions
    my $nl_arg_valid;
    eval { $nl_arg_valid = $definition->validate($nl_arg); };
    if ($@) {
	die "$ProgName - ERROR: Invalid namelist variable in commandline arg '-namelist'.\n $@";
    }

    # Merge input values into namelist.  Previously specified values have higher precedence
    # and are not overwritten.
    $nl->merge_nl($nl_arg_valid);
}

# Process the -infile arg.

if (defined $opts{'infile'}) {
    # Parse namelist input from a file
    my $nl_infile = Build::Namelist->new($opts{'infile'});

    # Validate input namelist -- trap exceptions
    my $nl_infile_valid;
    eval { $nl_infile_valid = $definition->validate($nl_infile); };
    if ($@) {
	die "$ProgName - ERROR: Invalid namelist variable in '-infile' $opts{'infile'}.\n $@";
    }

    # Merge input values into namelist.  Previously specified values have higher precedence
    # and are not overwritten.
    $nl->merge_nl($nl_infile_valid);
}

# Process the -use_case arg.
# Declare global symbol $uc_defaults even if it's not defined, because we check whether it's
# defined before using it in the get_default_value method below.

my $uc_defaults;
if (defined $use_case_file) {

    # The use case definition is contained in an xml file with the same format as the defaults file.
    # Create a new NamelistDefaults object.
    $uc_defaults = Build::NamelistDefaults->new($use_case_file, $cfg);

    # Loop over the variables specified in the use case.
    # A defaults file may contain information for variables that are not namelist variables.
    # Check each name in the defaults file, and add each namelist variable to the namelist.
    my @vars = $uc_defaults->get_variable_names();
    VAR: foreach my $var (@vars) {

	# Query the definition to find which group the variable belongs to.  Skip if not found.
	my $group = $definition->get_group_name($var);
	if ($group) {
	    # Get the value of $var from the use case defaults
	    my $val = $uc_defaults->get_value($var);
	    if (defined $val) {
		add_default($nl, $var, 'val'=>$val);
	    }
	    # If the use case doesn't provide a default value, then just ignore.  This allows the
	    # use case mechanism to provide defaults for some attributes (e.g. a specific grid), and
	    # not for others.
	}
    }
}

#-----------------------------------------------------------------------------------------------

# Add default values for required namelist variables that have not been previously set.
# This is done either by using the namelist default object, or directly with inline logic.

# Are we building namelists for the cpl6 scripts, the cpl7 scripts, or for standalone CAM 
# scripts.
my $cpl7_build = $cfg->get('ccsm_seq');
my $ccsm_build = $cpl7_build;
my $cam_build  = $ccsm_build ? 0 : 1;
if ($print>=2) { 
    if ($cpl7_build) {
	print "Writing namelist files for cpl7 scripts$eol";
    }
    elsif ($cam_build) {
	print "Writing namelist files for cam standalone scripts$eol";
    }
}


# Start with the driver component.  These settings are communicated by the driver to
# all subcomponents.

############################
# namelist group: ccsm_pes #
############################

# By default the driver sets all components to use all tasks.  This is the
# appropriate default for the standalone CAM configuration.

# By default the driver sets all components to use 1 thread.  This should be
# reset either to user specified values, or by default use the OMP_NUM_THREADS
# environment variable or the nthreads configuration parameter to set the thread
# count for each component.

# Is this an smp build?
my $smp = $cfg->get('smp');
if ($smp) {

    # If user has set the specific variables for component threads on the commandline
    # those values will be used.  If not, the highest precedence will be given the the
    # value of the environment variable OMP_NUM_THREADS.  That is because the number of
    # threads specified on the configure commandline is used to determine an appropriate
    # decomposition for the CICE model, but that decomposition may be valid for a range
    # of ntask/nthread settings.

    # Check for OMP_NUM_THREADS
    my $nthreads;
    if (defined $ENV{'OMP_NUM_THREADS'}) {
	$nthreads = $ENV{'OMP_NUM_THREADS'};
    }
    # Has the number of threads been set by configure
    elsif (defined $cfg->get('nthreads')) {
	$nthreads = $cfg->get('nthreads');
    }

    if ($nthreads) {
	add_default($nl, 'atm_nthreads', 'val'=>$nthreads);
	add_default($nl, 'cpl_nthreads', 'val'=>$nthreads);
	add_default($nl, 'ice_nthreads', 'val'=>$nthreads);
	add_default($nl, 'lnd_nthreads', 'val'=>$nthreads);
	add_default($nl, 'ocn_nthreads', 'val'=>$nthreads);
    }
}

#######################################
# namelist group: seq_infodata_inparm #
#######################################
my $phys_mode_flags = 0;
my $adia_mode = 0;
my $aqua_mode = 0;

# Case name
add_default($nl, 'case_name');

# Run type
add_default($nl, 'start_type');

# Adiabatic or Ideal physics
my $phys = $cfg->get('phys');
if ($phys eq 'adiabatic') { add_default($nl, 'atm_adiabatic',  'val'=>'.true.'); }
if ($phys eq 'ideal')     { add_default($nl, 'atm_ideal_phys', 'val'=>'.true.'); }

# Aqua planet 

my $aqua_flag = $cfg->get('ocn');
my $nl_aqua_flag = $nl->get_value('aqua_planet');

if ($aqua_flag  eq 'aquaplanet') {
    $aqua_mode = 1;
}

# Consistency check between configuration and namelist variables used for aquaplanet.
if ($aqua_mode and defined $nl_aqua_flag  and ($nl_aqua_flag  =~ m/$FALSE/io)) {
   die "$ProgName - ERROR: Configured CAM for aquaplanet but user set aqua_planet to FALSE in the namelist. \n";
}
if (!($aqua_mode) and defined $nl_aqua_flag  and ($nl_aqua_flag  =~ m/$TRUE/io)) {
   die "$ProgName - ERROR: CAM not configured for aquaplanet but user has set aqua_planet to TRUE in the namelist. \n";
}

if ($aqua_mode) {
    add_default($nl, 'aqua_planet', 'val'=>'.true.');
    add_default($nl, 'aqua_planet_sst', 'val'=>'1');
    ++$phys_mode_flags;
}

# Consistency check for namelist variables used to implement physics modes.
my $phys_flag = $nl->get_value('atm_adiabatic');
if (defined $phys_flag  and ($phys_flag  =~ m/$TRUE/io)) {
    $adia_mode = 1;
    ++$phys_mode_flags;
}
my $ideal_mode = 0;
$phys_flag = $nl->get_value('atm_ideal_phys');
if (defined $phys_flag  and ($phys_flag  =~ m/$TRUE/io)) {
    $ideal_mode = 1;
    ++$phys_mode_flags;
}
if ($phys_mode_flags > 1) {
    die "$ProgName - ERROR: Only one of the variables atm_adiabatic, atm_ideal_phys, and aqua_planet can be set .true. \n";
}

# Set convenience flag to indicate that one of the ideal, adiabatic, or 
# aqua-planet modes is being used.
my $ideal_or_adia_or_aqua = 0;
if ($phys_mode_flags == 1) { $ideal_or_adia_or_aqua = 1; }

# Set some flags that determine whether or not build-namelist will produce output
# for non-atm components.

# Start by checking how the executable was configured.


# If the physics mode is one of adiabatic, ideal, or aqua_planet, then the
# land and sea ice components are inactive (whether the executable was configured
# with them or not).
my $lnd_active = 1;
my $ice_active = 1;
if ($ideal_or_adia_or_aqua) {
    $lnd_active = 0;
    $ice_active = 0;
}

# Note that the prescribed SSTs
# used by aqua-planet mode are implemented in the cam_aqua component.)

# Single column mode
my $scam = $cfg->get('scam');
if ($scam) {
    add_default($nl, 'single_column', 'val'=>'.true.');
    my $scmlat = $nl->get_value('scmlat');
    my $scmlon = $nl->get_value('scmlon');
    unless (defined $scmlat and defined $scmlon) {
	die "$ProgName - ERROR: In SCAM mode both scmlat and scmlon namelist variables must be set. \n";
    }
}

######################################
# namelist group: seq_timemgr_inparm #
######################################

# Length of simulation
add_default($nl, 'stop_option');
unless (defined $nl->get_value('stop_ymd')) { add_default($nl, 'stop_n'); }

# Restart interval
add_default($nl, 'restart_option');

# Start date
# The following check should be done for CAM standalone builds only.
if ($cam_build) {
    # When the user specifies ncdata there is no default for start_ymd since there is
    # no way to determine the corresponding date of the ncdata file.  So only add
    # a default for start_ymd if the user has not specified ncdata.
    if (defined $nl->get_value('ncdata')) {
	unless (defined $nl->get_value('start_ymd')) {
	    die "$ProgName - ERROR: It is not allowed to set ncdata without also setting start_ymd. \n";
	}
    }
    else {
	add_default($nl, 'start_ymd');
    }
}

# Orbit (if not coupled)
# If orbital parameters have not been specified then check for orbit year.
# If orbit year has not been specified, then set a default value.
if ($cam_build) {
    if (not defined $nl->get_value('orb_obliq') or
	not defined $nl->get_value('orb_eccen') or
	not defined $nl->get_value('orb_mvelp')     ) {
	if (not defined $nl->get_value('orb_iyear')) {
	    add_default($nl, 'orb_iyear');
	}
    }
}

add_default($nl, 'ocean_tight_coupling');

# Coupling interval
# The default is for CAM to couple to the surface components every CAM timestep.
# So start by making sure CAM's dtime is set.
add_default($nl, 'dtime');
add_default($nl, 'atm_cpl_dt', 'val'=>$nl->get_value('dtime'));

###############################
# namelist group: prof_inparm #
###############################

# The default for timing output is to use separate files for each MPI task.
# Change default to using a single file.
add_default($nl, 'profile_single_file', 'val'=>'.true.');

#-----------------------------------------------------------------------------------------------
# Add defaults for the CAM component 

my $chem = $cfg->get('chem');
my $waccm_chem = ($chem eq 'waccm_mozart' or $chem eq 'waccm_mozart_v1' or $chem eq 'waccm_ghg');
my $waccm_phys = $cfg->get('waccm_phys');

my $prog_species = $cfg->get('prog_species');
my $prog_ghg1    = ($chem =~ "trop_mozart" or $chem =~ "trop_strat" or $chem =~ "ghg" or $prog_species =~ "GHG");
my $prog_ghg2    = (                          $chem =~ "ghg" or $prog_species =~ "GHG");
my $ghg_chem     = ($chem =~ "ghg");
my $aero_chem    = ($chem =~ "aero" or $chem eq 'trop_mozart'  or $chem =~ 'trop_strat' or $chem eq 'trop_bam');

my $chem_rad_passive = ($nl->get_value('chem_rad_passive') =~ /$TRUE/io);
my $ipcc_aircraft_emis = ($nl->get_value('ipcc_aircraft_emis') =~ /$TRUE/io);

my $rad_prog_ocarb = (($prog_species =~ "OC"   or $aero_chem) and !($chem_rad_passive));
my $rad_prog_bcarb = (($prog_species =~ "BC"   or $aero_chem) and !($chem_rad_passive));
my $rad_prog_sulf  = (($prog_species =~ "SO4"  or $aero_chem or $chem =~ "super_fast_llnl") and !($chem_rad_passive));
my $rad_prog_dust  = (($prog_species =~ "DST"  or $aero_chem) and !($chem_rad_passive));
my $rad_prog_sslt  = (($prog_species =~ "SSLT" or $aero_chem) and !($chem_rad_passive));
my $rad_prog_ozone = (($chem =~ "mozart" or $chem =~ "trop_strat" or $chem =~ "super_fast_llnl") and !($chem_rad_passive));

my $clm_vocsrc = $cfg->get('clm_vocsrc');

# Check for eruptive volcano emissions.  These will be radiatively active by default, but
# only if using BAM and the camrt radiation package (cam4 physics)
# or if using MAM and the rrtmg package  (cam5 physics)
my $rad_volcaero = $nl->get_value('prescribed_volcaero_file') ? 1 : 0;

#################
# CAM namelists #
#################

# Print conservation errors
# Turn this off for PERGRO runs or for WACCM runs
if ($cfg->get('pergro') or ($chem ne 'none')) {
    add_default($nl, 'print_energy_errors', 'val'=>'.false.');
}

# Chemistry deposition lists
if ( ($chem ne 'none') or ( $prog_species ) ){
    my $chem_src_dir = $cfg->get('chem_src_dir');

    my ($gas_wetdep_list, $aer_wetdep_list, $drydep_list, $mam_drydep_list) =
      set_dep_lists( $cfgdir, $chem, $chem_src_dir, $print ); 

    # kluge to keep CMIP5 simulations the same
    if ( $chem eq 'waccm_mozart_v1' ) {
      $gas_wetdep_list = "'H2O2','HNO3','CH2O','CH3OOH','HO2NO2',"
	  ."'CLONO2','BRONO2','HCL','N2O5','HOCL','HOBR','HBR'";
    }
    if ($gas_wetdep_list){ add_default($nl, 'gas_wetdep_list', 'val'=>$gas_wetdep_list ); }

    if ($aer_wetdep_list){ add_default($nl, 'aer_wetdep_list', 'val'=>$aer_wetdep_list ); }
    if ($drydep_list){ add_default($nl, 'drydep_list', 'val'=>$drydep_list ); }
    if ($mam_drydep_list){ add_default($nl, 'aer_drydep_list', 'val'=>$mam_drydep_list ); }

    if ( $drydep_list ) {
      add_default($nl, 'depvel_file' );
      add_default($nl, 'depvel_lnd_file' );
      add_default($nl, 'clim_soilw_file' );
      add_default($nl, 'season_wes_file' );
    }
}

# Initial conditions
#
# Most of the attributes that are matched to determine the default initial file are 
# build time options that come from the configuration object.  But there are also a couple
# of run time options that need to be considered.  Set those in the optional hash argument
# to add_default.
#
# Check for aqua-planet run:
my %atts = ();

# The initial date is an attribute in the defaults file which should be matched unless
# the user explicitly requests to ignore the initial date via the -ignore_ic_date option, 
# or just ignore the year of the initial date via the -ignore_ic_year option.
my $ic_date = $nl->get_value('start_ymd');
if ($opts{'ignore_ic_date'}) {
    # Don't set any attribute for date matching.  By putting this option first it
    # will take precedence in the case that the user has set both -ignore_ic_date
    # and -ignore_ic_year
}
elsif ($opts{'ignore_ic_year'}) {
    $atts{'ic_md'} = $ic_date;
}
else {
    # if neither option specified then match full date
    $atts{'ic_ymd'} = $ic_date;
}
add_default($nl, 'ncdata', %atts);

#
# Simulated years: sim_year and sim_year_start
#
# sim_year
# This is used to identify appropriate defaults for climatological or transient
# forcing datasets.  If user hasn't specified then default to 2000 (present day climatology).
my $sim_year = '2000';
if (defined $nl->get_value('sim_year')) {
    $sim_year = $nl->get_value('sim_year');
    # strip quotes to use the value in attribute matching.
    $sim_year =~ s/['"]//g; #"'
}

# sim_year_start
# If sim_year is input as a range of years, then select the first year
# to use with some datasets
my $sim_year_start = $sim_year;
if ($sim_year =~ /(\d+)-(\d+)/) {
    $sim_year_start = $1;
}

# Topography
add_default($nl, 'bnd_topo', 'nofail'=>1);

# Tropopause climatology
add_default($nl, 'tropopause_climo_file');

# Solar irradiance
if ($chem eq 'none') {
   # this preserves the cam3 and cam4 configurations which do not have chemistry
    unless (defined $nl->get_value('solar_data_file')) {
       add_default($nl, 'solar_const');
    }
} else {
   # use solar data file as the default for all chem conifigurations
    unless (defined $nl->get_value('solar_const')) {
       add_default($nl, 'solar_data_file');
       add_default($nl, 'solar_const', 'val'=>'-9999.');    
    }
}

# Radiation
my $rad_pkg = $cfg->get('rad');
if ($rad_pkg eq 'camrt') {
    # Only needed for CAM3/4 RT code
    # Absorptivity and emissivity data
    add_default($nl, 'absems_data');
}

if (($chem =~ /waccm_mozart/ )or($rad_pkg =~ /rrtmg/)) {
    add_default($nl, 'solar_htng_spctrl_scl', 'val'=>'.true.');
} else {
    add_default($nl, 'solar_htng_spctrl_scl', 'val'=>'.false.');
}


# COSP simulator
if ($cfg->get('cosp')) {
    add_default($nl, 'docosp', 'val'=>'.true.');
}


# Constituents
# ============

# Carbon cycle constituents
my $co2_cycle = $cfg->get('co2_cycle');

if ($co2_cycle) {

    # co2_flag turns on the co2_cycle code in CAM
    add_default($nl, 'co2_flag', 'val'=>'.true.');


    # Supply a fossil fuel dataset if the co2_cycle is active and it's a
    # transient run ...
    if ($sim_year =~ /(\d+)-(\d+)/) {

	add_default($nl, 'co2_readflux_fuel', 'val'=>'.true.');

	# Check whether user has explicitly turned off reading the fossil fuel dataset.
	# (user specification has higher precedence than the true value set above)
	if ($nl->get_value('co2_readflux_fuel') =~ /$TRUE/io) {
	    add_default($nl, 'co2flux_fuel_file', 'sim_year'=>$sim_year);
	}
    }
}

# By default the prognostic co2_cycle CO2 will be radiative active, unless the
# the user sets this override variable.  This is used below to construct the
# rad_climate variable.
my $co2_cycle_rad_passive = ($nl->get_value('co2_cycle_rad_passive') =~ /$TRUE/io);


# If test tracers have been requested, set tracers_flag=.true.
if ($cfg->get('nadv_tt')) { add_default($nl, 'tracers_flag', 'val'=>'.true.'); }
if ($cfg->get('age_of_air_trcs')) { add_default($nl, 'aoa_tracers_flag', 'val'=>'.true.'); }

# If phys option is "cam3" then turn on the CAM3 prescribed ozone and aerosols
if ($phys eq 'cam3' and !$aqua_mode) {
    add_default($nl, 'cam3_ozone_data_on', 'val'=>'.true.');
    add_default($nl, 'cam3_aero_data_on', 'val'=>'.true.');
}

# Defaults for radiatively active constituents

my $cam3_ozone_data = $FALSE;
my $cam3_aero_data = $FALSE;

my $moz_ozone_data = $FALSE;
if (!$rad_prog_ozone) {
    $moz_ozone_data = $TRUE;
}

my $moz_aero_data = $FALSE;
if (!($rad_prog_ocarb) or !($rad_prog_bcarb) or !($rad_prog_sulf) or !($rad_prog_dust) or !($rad_prog_sslt)){
    $moz_aero_data = $TRUE;
}

# CAM3 prescribed ozone only by request
if (defined $nl->get_value('cam3_ozone_data_on') and
    $nl->get_value('cam3_ozone_data_on') =~ /$TRUE/io) {
    add_default($nl, 'bndtvo');
    $cam3_ozone_data = $TRUE;
    $moz_ozone_data = $FALSE;
}

# CAM3 prescribed aerosols only by request
if (defined $nl->get_value('cam3_aero_data_on') and
    $nl->get_value('cam3_aero_data_on') =~ /$TRUE/io) {

    # CAM3 aerosol mass climatology dataset (horizontal resolution dependent)
    add_default($nl, 'bndtvaer');
    $cam3_aero_data = $TRUE;
    $moz_aero_data = $FALSE;
}

# The aerosol optics depend on which radiative transfer model is used due to differing
# wavelength bands used.
my $rrtmg = $rad_pkg eq 'rrtmg' ? 1 : 0;

# Use @aero_names for the lists of aerosol names that are used by the modules responsible
# for putting the constituents into either the state or the physics buffer.

my @aero_names = ();
my @aerosources = ();
my $radval = "'P_Q:H2O'";

if ($chem_rad_passive) {
    add_default($nl, 'atm_dep_flux', 'val'=>'.false.');
}

if ($chem =~ /waccm_mozart/ and !$chem_rad_passive) {
    $radval .= ",'P_O2:O2','P_CO2:CO2'";
}
elsif ($chem =~ /trop_strat_bam/ and !$chem_rad_passive) {
    $radval .= ",'D_O2:O2','P_CO2:CO2'";
}
elsif ($co2_cycle and !$co2_cycle_rad_passive) {
    $radval .= ",'D_O2:O2','P_CO2:CO2'";
}
else {
    $radval .= ",'D_O2:O2','D_CO2:CO2'";
}

if ($rad_prog_ozone) {
    $radval .= ",'P_O3:O3'";
} elsif ($moz_ozone_data =~ /$TRUE/io) {
    $radval .= ",'D_ozone:O3'";
    unless (defined $nl->get_value('prescribed_ozone_file')) {
        add_default($nl, 'prescribed_ozone_datapath');
        add_default($nl, 'prescribed_ozone_file');
        add_default($nl, 'prescribed_ozone_name');
        add_default($nl, 'prescribed_ozone_type');
        add_default($nl, 'prescribed_ozone_cycle_yr');
    }
} elsif ($cam3_ozone_data =~ /$TRUE/io) {
    $radval .= ",'D_O3:O3'";
} else {
    die "ERROR: can not set ozone rad_climate specification\n";
}

if (($chem =~ /super_fast_llnl/) and !$chem_rad_passive ) {
    $radval .= ",'D_N2O:N2O','D_prsd_ch4:CH4','D_CFC11:CFC11','D_CFC12:CFC12'";
} elsif (($chem =~ /trop_strat_bam/) and !$chem_rad_passive ) {
    $radval .= ",'P_N2O:N2O','P_CH4:CH4','P_CFC11:CFC11','P_CFC12:CFC12'";
} elsif (($chem =~ /waccm_mozart/) and !$chem_rad_passive ) {
    $radval .= ",'P_N2O:N2O','P_CH4:CH4','D_CFC11STAR:CFC11','P_CFC12:CFC12'";
} elsif ($prog_ghg1 and $prog_ghg2 and !$chem_rad_passive ) {
    $radval .= ",'P_N2O:N2O','P_CH4:CH4','P_CFC11:CFC11','P_CFC12:CFC12'";
} elsif ($prog_ghg1 and !$prog_ghg2  and !$chem_rad_passive ) {
    $radval .= ",'P_N2O:N2O','P_CH4:CH4','D_CFC11:CFC11','D_CFC12:CFC12'";
} else {
    $radval .= ",'D_N2O:N2O','D_CH4:CH4','D_CFC11:CFC11','D_CFC12:CFC12'";
}

my $aer_model = "bam";

## ?? MAM rad_climate settings ?? -- cannot be made radiatively passive
if ($chem =~ /_mam/ ) {

    $aer_model = "mam";

    # Currently MAM only works with these rad_climate settings 
    # -- cannont be configured to be radiatively passive.
    if ($chem eq 'trop_mam3' or $chem eq 'super_fast_llnl_mam3') {
        # CAM-Chem prognostic modal aerosols.
        @aero_names = qw(so4_a1 pom_a1 soa_a1 bc_a1 dst_a1 ncl_a1 so4_a2 soa_a2 ncl_a2 dst_a3 ncl_a3 so4_a3);
	push(@aerosources, "P_",  "P_",  "P_", "P_",  "P_",  "P_",  "P_",  "P_",  "P_",  "P_",  "P_",  "P_");
    } elsif ($chem eq 'trop_mam7') {
        # CAM-Chem prognostic modal aerosols.
	@aero_names = qw(so4_a1 dst_a5 bc_a1 pom_a1 soa_a1 ncl_a1 ncl_a4);
	push(@aerosources, "P_",  "P_", "P_",  "P_",  "P_",  "P_",  "P_");
    }

    if ($rad_volcaero) {
    push(@aero_names, "VOLC_MMR");
    push(@aerosources, "D_");
    }
    
}else{

  if ($rad_prog_sulf) {
    push(@aero_names, "SO4" );
    push(@aerosources, "P_" );
  } elsif ($moz_aero_data =~ /$TRUE/io) {
    push(@aero_names, "sulf");
    push(@aerosources, "D_" );
  } elsif ($cam3_aero_data =~ /$TRUE/io) {
    push(@aero_names, "cam3_sul" );
    push(@aerosources, "D_" );
  } else {
    die "ERROR: can not set sulf rad_climate specification\n";
  }

  if ($rad_prog_dust) {
    push(@aero_names, "DST01", "DST02", "DST03", "DST04" );
    push(@aerosources, "P_", "P_", "P_", "P_" );
  } elsif ($moz_aero_data =~ /$TRUE/io) {
    push(@aero_names, "dust1", "dust2", "dust3", "dust4");
    push(@aerosources, "D_", "D_", "D_", "D_" );
  } elsif ($cam3_aero_data =~ /$TRUE/io) {
    push(@aero_names, "cam3_dust1", "cam3_dust2", "cam3_dust3", "cam3_dust4" );
    push(@aerosources, "D_", "D_", "D_", "D_" );
  } else {
    die "ERROR: can not set dust rad_climate specification\n";
  }

  if ($rad_prog_bcarb) {
    push(@aero_names, "CB1", "CB2" );
    push(@aerosources, "P_", "P_" );
  } elsif ($moz_aero_data =~ /$TRUE/io) {
    push(@aero_names, "bcar1", "bcar2");
    push(@aerosources, "D_", "D_" );
  } elsif ($cam3_aero_data =~ /$TRUE/io) {
    push(@aero_names, "cam3_bcpho", "cam3_bcphi");
    push(@aerosources, "D_", "D_" );
  } else {
    die "ERROR: can not set black carbon rad_climate specification\n";
  }

  if ($rad_prog_ocarb) {
    push(@aero_names, "OC1", "OC2" );
    push(@aerosources, "P_", "P_" );
  } elsif ($moz_aero_data =~ /$TRUE/io) {
    push(@aero_names, "ocar1", "ocar2");
    push(@aerosources, "D_", "D_" );
  } elsif ($cam3_aero_data =~ /$TRUE/io) {
    push(@aero_names, "cam3_ocpho", "cam3_ocphi");
    push(@aerosources, "D_", "D_" );
  } else {
    die "ERROR: can not set organic carbon rad_climate specification\n";
  }

  if ($rad_prog_sslt) {
    if ($rrtmg) {
	push(@aero_names, "SSLT01", "SSLT02", "SSLT03", "SSLT04");
	push(@aerosources, "P_", "P_", "P_", "P_" );
    } else {
	push(@aero_names, "SSLTA", "SSLTC");
	push(@aerosources, "D_", "D_");
    }
  } elsif ($moz_aero_data =~ /$TRUE/io ) {
    if ($rrtmg) {
        push(@aero_names, "sslt1", "sslt2", "sslt3", "sslt4");
        push(@aerosources, "D_", "D_", "D_", "D_" );
    } else {
	push(@aero_names, "SSLTA", "SSLTC");
	push(@aerosources, "D_", "D_");
    }
  } elsif ($cam3_aero_data =~ /$TRUE/io ) {
    push(@aero_names, "cam3_ssam", "cam3_sscm");
    push(@aerosources, "D_", "D_" );
  } else {
    die "ERROR: can not set sslt rad_climate specification\n";
  }

  if ($rad_volcaero and !$rrtmg) {
    push(@aero_names, "VOLC_MMR");
    push(@aerosources, "D_");
  }

  if ($moz_aero_data =~ /$TRUE/io ) {
    my $val = "'sulf:SO4','bcar1:CB1','bcar2:CB2','ocar1:OC1','ocar2:OC2',"
            . "'sslt1:SSLT01','sslt2:SSLT02','sslt3:SSLT03','sslt4:SSLT04',"
	    . "'dust1:DST01','dust2:DST02','dust3:DST03','dust4:DST04'";
    add_default($nl, 'prescribed_aero_specifier', 'val'=>$val);
    unless (defined $nl->get_value('prescribed_aero_file')) {
      my @settings = ('prescribed_aero_datapath', 'prescribed_aero_file', 'prescribed_aero_type',
	              'prescribed_aero_cycle_yr');
      foreach my $setting (@settings) {
	add_default($nl, $setting);
      }
    }
  }

  if (($moz_aero_data =~ /$TRUE/io ) or ($cam3_aero_data =~ /$TRUE/io )) {
    unless (defined $nl->get_value('aerodep_flx_file')) {
      my @settings = ('aerodep_flx_datapath', 'aerodep_flx_file', 'aerodep_flx_type',
	              'aerodep_flx_cycle_yr');
      foreach my $setting (@settings) {
	add_default($nl, $setting);
      }
    }
  }
}

# Construct the aerosol part of the rad_climate string array by looping over
# the aerosol names and getting the default properties file for each:
foreach my $name (@aero_names) {
    my $rel_filepath = get_default_value("${aer_model}_${name}");
    my $abs_filepath = set_abs_filepath($rel_filepath, $inputdata_rootdir);
    my $source = shift(@aerosources);
    $radval .= "," . quote_string($source . $name . ":" . $abs_filepath);
}

# Stratospheric sulfur aerosols 
if ($nl->get_value('strat_aero_feedback') =~ /$TRUE/io) {
    my $name = "H2SO4M";
    my $rel_filepath = get_default_value("bam_VOLC_MMR");
    my $abs_filepath = set_abs_filepath($rel_filepath, $inputdata_rootdir);
    $radval .= "," . quote_string("D_" . $name . ":" . $abs_filepath);
}

add_default($nl, 'rad_climate', 'val'=>$radval);

# Cloud optics
if ($rrtmg) {
    add_default($nl, 'liqcldoptics');
    add_default($nl, 'icecldoptics');
    add_default($nl, 'liqopticsfile');
    add_default($nl, 'iceopticsfile');
}

# Modal optics
my $modal_aero = '';  # modal aerosol representation ('3mode' or '7mode')
if ($chem eq 'trop_mam3' or $chem eq 'super_fast_llnl_mam3') {
    $modal_aero = '3mode';
}
elsif ($chem eq 'trop_mam7') {
    $modal_aero = '7mode';
}
if ($modal_aero) {
    add_default($nl, 'modal_optics_file', 'aero'=>$modal_aero);
    add_default($nl, 'water_refindex_file');
}

# Volcanic Aerosol Mass climatology dataset
if ($nl->get_value('strat_volcanic')) { add_default($nl, 'bndtvvolc'); }

# Greenhouse gas production/loss rates
if ($ghg_chem) {
    add_default($nl, 'bndtvg');
    add_default($nl, 'ghg_chem', 'val'=>".true.");
}

# WACCM-GHG options
if ($chem eq 'waccm_ghg') {
    # *** Note *** this dataset only needed for waccm_ghg.
    # O2,O1,N2, CO2 Constituents for non-LTE calculations and heating rates below 200 nm
    unless (defined $nl->get_value('waccm_forcing_file')) {
	add_default($nl, 'waccm_forcing_datapath');
	add_default($nl, 'waccm_forcing_file');
    }
    add_default($nl, 'nlte_use_mo', 'val'=>".false.");
    add_default($nl, 'h2orates');
    add_default($nl, 'solar_parms_file');
    add_default($nl, 'do_circulation_diags', 'val'=>'.true.');   
}

if ( $prog_species ) {
    my $ddval;
    my $emisval;
    my $xfrcval;
    my $emisfirst = 1; my $emispre = "";
    my $xfrcfirst = 1; my $xfrcpre = "";
    if ( $prog_species =~ /SO4/ )  { 
      my %emis = ('DMS -> ' => 'dms_emis_file',
                  'SO2 -> ' => 'so2_emis_file',
                  'SO4 -> ' => 'so4_emis_file', );
      foreach my $id (sort keys %emis) {
        my $rel_filepath = get_default_value($emis{$id});
        my $abs_filepath = set_abs_filepath($rel_filepath, $inputdata_rootdir);
        $emisval .= $emispre . quote_string($id . $abs_filepath);
        if ($emisfirst) { $emispre = ","; $emisfirst = 0; }
      }
      my %xfrc = ('SO2 -> ' => 'so2_vrt_emis_file',
		  'SO4 -> ' => 'so4_vrt_emis_file', );
      foreach my $id (sort keys %xfrc) {
        my $rel_filepath = get_default_value($xfrc{$id});
        my $abs_filepath = set_abs_filepath($rel_filepath, $inputdata_rootdir);
        $xfrcval .= $xfrcpre . quote_string($id . $abs_filepath);
        if ($xfrcfirst) { $xfrcpre = ","; $xfrcfirst = 0; }
      }
      # Prescribed oxidants
      my $val = "'O3','OH','NO3','HO2'";
      add_default($nl, 'tracer_cnst_specifier', 'val'=>$val);
      my @files = ( 'tracer_cnst_datapath','tracer_cnst_file');
      foreach my $file (@files) {
	add_default($nl, $file, 'ver'=>'fixed_ox');
      }
      unless (defined $nl->get_value('tracer_cnst_type')) {
	  add_default($nl, 'tracer_cnst_type', 'ver'=>'fixed_ox');
	  add_default($nl, 'tracer_cnst_cycle_yr','ver'=>'fixed_ox');
      }
      add_default($nl, 'use_cam_sulfchem', 'val'=>".true.");
    }

    if ( $prog_species =~ /OC/ )  { 
      my %emis = ('OC1 -> ' => 'oc1_emis_file', );
      foreach my $id (sort keys %emis) {
        my $rel_filepath = get_default_value($emis{$id});
        my $abs_filepath = set_abs_filepath($rel_filepath, $inputdata_rootdir);
        $emisval .= $emispre . quote_string($id . $abs_filepath);
        if ($emisfirst) { $emispre = ","; $emisfirst = 0; }
      }
    }
    if ( $prog_species =~ /BC/ )  { 
      my %emis = ('CB1 -> ' => 'cb1_emis_file', );
      foreach my $id (sort keys %emis) {
        my $rel_filepath = get_default_value($emis{$id});
        my $abs_filepath = set_abs_filepath($rel_filepath, $inputdata_rootdir);
        $emisval .= $emispre . quote_string($id . $abs_filepath);
        if ($emisfirst) { $emispre = ","; $emisfirst = 0; }
      }
    }
    if ( $prog_species =~ /GHG/ )  { 
      add_default($nl, 'ghg_chem', 'val'=>".true.");
      add_default($nl, 'bndtvg');
    }
    if ( $prog_species =~ /DST/ )  { 
      add_default($nl, 'soil_erod' );
    }

    if ( $emisval ) {
      unless (defined $nl->get_value('srf_emis_type')) {
	add_default($nl, 'srf_emis_type',      'val'=>'CYCLICAL');
	add_default($nl, 'srf_emis_cycle_yr',  'val'=>'2000');
      }
    }
    if ( $xfrcval ) {
      add_default($nl, 'ext_frc_specifier',  'val'=>$xfrcval);
      add_default($nl, 'ext_frc_type',  'val'=>'CYCLICAL');
    }
} 

# Tropospheric full chemistry options
if ($chem eq 'trop_mozart' or $chem eq 'trop_strat_bam_v1') {

    # Surface emission datasets:
    my $val;
    my %species = ('CH3COCH3 -> ' => 'acetone_emis_file',
		   'BIGALK   -> ' => 'bigalk_emis_file',
		   'BIGENE   -> ' => 'bigene_emis_file',
		   'C2H4     -> ' => 'c2h4_emis_file',
		   'C2H5OH   -> ' => 'c2h5oh_emis_file',
		   'C2H6     -> ' => 'c2h6_emis_file',
		   'C3H6     -> ' => 'c3h6_emis_file',
		   'C3H8     -> ' => 'c3h8_emis_file',
		   'CB1      -> ' => 'cb1_emis_file',
		   'CB2      -> ' => 'cb2_emis_file',
		   'CH2O     -> ' => 'ch2o_emis_file',
		   'CH3CHO   -> ' => 'ch3cho_emis_file',
		   'CH3OH    -> ' => 'ch3oh_emis_file',
		   'CO       -> ' => 'co_emis_file',
		   'DMS      -> ' => 'dms_emis_file',
		   'MEK      -> ' => 'mek_emis_file',
		   'NH3      -> ' => 'nh3_emis_file',
		   'NO       -> ' => 'nox_emis_file',
		   'OC1      -> ' => 'oc1_emis_file',
		   'OC2      -> ' => 'oc2_emis_file',
		   'SO2      -> ' => 'so2_emis_file',
		   'TOLUENE  -> ' => 'toluene_emis_file',
		   'HCN      -> ' => 'hcn_emis_file',
		   'CH3CN    -> ' => 'ch3cn_emis_file',
		   'C2H2     -> ' => 'c2h2_emis_file',
		   'CH3COOH  -> ' => 'ch3cooh_emis_file' );
    if ( ! $clm_vocsrc ) {
	%species = (%species , 
                   'C10H16   -> ' => 'c10h16_emis_file',
		   'ISOP     -> ' => 'isop_emis_file',  );
    }

    my $first = 1; my $pre = "";
    foreach my $id (sort keys %species) {
	my $rel_filepath = get_default_value($species{$id});
	my $abs_filepath = set_abs_filepath($rel_filepath, $inputdata_rootdir);
	$val .= $pre . quote_string($id . $abs_filepath);
	if ($first) {
	    $pre = ",";
	    $first = 0;
	}
    }
    add_default($nl, 'srf_emis_specifier', 'val'=>$val);
    unless (defined $nl->get_value('srf_emis_type')) {
	add_default($nl, 'srf_emis_type',    'val'=>'CYCLICAL');
	add_default($nl, 'srf_emis_cycle_yr','val'=>'2000');
    }

    # aircraft emission datasets:
    %species = ();
    if ($chem eq 'trop_strat_bam_v1') {
      %species = ( 'NO2 -> ' => 'no2_aircraft_emis_file',
                   'CB1 -> ' => 'bc_aircraft_emis_file' );
    } else {
      %species = ('CO      -> ' => 'co_aircraft_emis',
	  	  'NO      -> ' => 'no_aircraft_emis',
                  'SO2     -> ' => 'so2_aircraft_emis', );
    }

    $first = 1;
    $pre = "";
    $val = "";
    foreach my $id (sort keys %species) {
	my $rel_filepath = get_default_value($species{$id});
	my $abs_filepath = set_abs_filepath($rel_filepath, $inputdata_rootdir);
	$val .= $pre . quote_string($id . $abs_filepath);
	if ($first) {
	    $pre = ",";
	    $first = 0;
	}
    }
    add_default($nl, 'ext_frc_specifier', 'val'=>$val);
    unless (defined $nl->get_value('ext_frc_type')) {
	add_default($nl, 'ext_frc_type',      'val'=>'CYCLICAL');
	add_default($nl, 'ext_frc_cycle_yr',  'val'=>'1999');
    }
    # 

    # 

    if ($chem eq 'trop_mozart') {
      # Species with fixed stratosphere values
      $val = "'O3','NO','NO2','HNO3','CO','CH4','N2O','N2O5'";
      add_default($nl, 'fstrat_list', 'val'=>$val);
      add_default($nl, 'fstrat_file');
    }

    # Species with fixed lower boundary
    if ($chem eq 'trop_strat_bam_v1') {
      $val = "'CCL4','CF2CLBR','CF3BR','CFC11','CFC113','CFC12','CH3BR','CH3CCL3','CH3CL','CH4','CO2','H2','HCFC22','N2O'";
    } else {
      $val = "'CH4','H2','N2O'";
    }
    add_default($nl, 'flbc_list', 'val'=>$val);
    unless (defined $nl->get_value('flbc_type')) {
	add_default($nl, 'flbc_type',  'val'=>'CYCLICAL');
	add_default($nl, 'flbc_cycle_yr',  'val'=>'2000');
    }

    # Datasets
    my @files = ( 'soil_erod', 'flbc_file', 
	         'xs_coef_file','xs_short_file','xs_long_file', 'rsf_file', 'exo_coldens_file', 'sulf_file', 'sad_file' );
    foreach my $file (@files) {
	add_default($nl, $file);
    }
    unless (defined $nl->get_value('sad_type')) {
	add_default($nl, 'sad_type',  'val'=>'CYCLICAL');
	add_default($nl, 'sad_cycle_yr',  'val'=>'2000');
    }
}

# Prognostic aerosols via CAM-Chem package.
# BAM settings
if ($chem eq 'trop_bam') {

    #
    add_default($nl, 'use_cam_sulfchem', 'val'=>'.true.');
    
    my %species;

    # Surface emission datasets:
    %species = ();
    %species = ('CB1      -> ' => 'cb1_emis_file',
		'DMS      -> ' => 'dms_emis_file',
		'OC1      -> ' => 'oc1_emis_file',
		'SO2      -> ' => 'so2_emis_file',
		'SO4      -> ' => 'so4_emis_file', );
    my $first = 1;
    my $pre = "";
    my $val = "";
    foreach my $id (sort keys %species) {
	my $rel_filepath = get_default_value($species{$id});
	my $abs_filepath = set_abs_filepath($rel_filepath, $inputdata_rootdir);
	$val .= $pre . quote_string($id . $abs_filepath);
	if ($first) {
	    $pre = ",";
	    $first = 0;
	}
    }
    add_default($nl, 'srf_emis_specifier', 'val'=>$val);
    add_default($nl, 'srf_emis_type',      'val'=>'CYCLICAL');
    
    # Surface emission datasets:
    %species = ();
    %species = ('SO2      -> ' => 'so2_vrt_emis_file',
		'SO4      -> ' => 'so4_vrt_emis_file', );
    $first = 1;
    $pre = "";
    $val = "";
    foreach my $id (sort keys %species) {
	my $rel_filepath = get_default_value($species{$id});
	my $abs_filepath = set_abs_filepath($rel_filepath, $inputdata_rootdir);
	$val .= $pre . quote_string($id . $abs_filepath);
	if ($first) {
	    $pre = ",";
	    $first = 0;
	}
    }
    add_default($nl, 'ext_frc_specifier', 'val'=>$val);
    add_default($nl, 'ext_frc_type',      'val'=>'CYCLICAL');

    # Prescribed species
    add_default($nl, 'tracer_cnst_specifier', 'val'=>"'O3','OH','NO3','HO2'");

    my @files = ('tracer_cnst_datapath', 'tracer_cnst_file');
    foreach my $file (@files) {
	add_default($nl, $file, 'ver'=>'fixed_ox');
    }
    unless (defined $nl->get_value('tracer_cnst_type')) {
	add_default($nl, 'tracer_cnst_type',    'ver'=>'fixed_ox');
	add_default($nl, 'tracer_cnst_cycle_yr','ver'=>'fixed_ox');
    }

    add_default($nl, 'fstrat_list', 'val'=>"' '");
    add_default($nl, 'flbc_list', 'val'=>"' '");

    # Datasets
    add_default($nl, 'soil_erod');
}

if ($chem eq 'super_fast_llnl') {
    # Surface emission datasets:
    my $val;
    my %species;
    %species = (
		'CH2O     -> ' => 'ch2o_emis_file',
		'CO       -> ' => 'co_emis_file',
		'DMS      -> ' => 'dms_emis_file',
		'NO       -> ' => 'nox_emis_file',
		'SO2      -> ' => 'so2_emis_file',
		);
    if ( ! $clm_vocsrc ) {
	%species = (%species , 
		   'ISOP     -> ' => 'isop_emis_file',  );
    }
    unless (defined $nl->get_value('srf_emis_type')) {
	add_default($nl, 'srf_emis_cycle_yr',  'val'=>'1997');
	add_default($nl, 'srf_emis_type',  'val'=>'CYCLICAL');
    }
    my $first = 1; my $pre = "";
    foreach my $id (sort keys %species) {
        my $rel_filepath = get_default_value($species{$id});
	my $abs_filepath = set_abs_filepath($rel_filepath, $inputdata_rootdir);
	$val .= $pre . quote_string($id . $abs_filepath);
	if ($first) {
	    $pre = ",";
	    $first = 0;
	}
    }
    add_default($nl, 'srf_emis_specifier', 'val'=>$val);

    # vertical emissions... 
    %species = ();
    %species = ('SO2 -> ' => 'so2_vrt_emis_file',
		'SO4 -> ' => 'so4_vrt_emis_file' );
    add_default($nl, 'ext_frc_type', 'val'=>"'CYCLICAL'");

    if ($ipcc_aircraft_emis) {
      %species = ('SO2 -> ' => 'so2_vrt_emis_file',
	  	  'SO4 -> ' => 'so4_vrt_emis_file',
                  'NO2 -> ' => 'no2_aircraft_emis_file' );
    }

    $first = 1; $pre = ""; $val = "";
    foreach my $id (sort keys %species) {
        my $rel_filepath = get_default_value($species{$id});
        my $abs_filepath = set_abs_filepath($rel_filepath, $inputdata_rootdir);
        $val .= $pre . quote_string($id . $abs_filepath);
        if ($first) {
            $pre = ",";
            $first = 0;
        }
    }
    add_default($nl, 'ext_frc_specifier', 'val'=>$val);

    # Species with fixed stratosphere values
    $val = "'NO','NO2','HNO3','CO'";
    add_default($nl, 'fstrat_list', 'val'=>$val);

    # Datasets
    my @files = ('airpl_emis_file', 'soil_erod', 'flbc_file', 'fstrat_file',
                 'sulf_file', 'tuv_xsect_file', 'o2_xsect_file', 'xs_long_file', 'rsf_file', 
                 'solar_parms_file',
                 'exo_coldens_file', 'sad_file', 'linoz_data_path' , 'linoz_data_file', 'chlorine_loading_file' );
    foreach my $file (@files) {
	add_default($nl, $file);
    }

    unless (defined $nl->get_value('sad_type')) {
	add_default($nl, 'sad_type',  'val'=>'CYCLICAL');
	add_default($nl, 'sad_cycle_yr',  'val'=>'1990');
    }
    unless (defined $nl->get_value('flbc_type')) {
	add_default($nl, 'flbc_type',  'val'=>'CYCLICAL');
	add_default($nl, 'flbc_cycle_yr',  'val'=>'1990');
    }
    unless (defined $nl->get_value('chlorine_loading_type')) {
	add_default($nl, 'chlorine_loading_type', 'val'=>'FIXED');
	add_default($nl, 'chlorine_loading_fixed_ymd','val'=>'19900101');
    }

    add_default($nl, 'xactive_prates', 'val'=>'.false.');

    # Prescribed methane
    my $val = "'CH4'";
    add_default($nl, 'tracer_cnst_specifier', 'val'=>$val);
    my @files = ( 'tracer_cnst_datapath','tracer_cnst_file', 'tracer_cnst_filelist');
    foreach my $file (@files) {
	add_default($nl, $file, 'ver'=>'fixed_ch4');
    }
    unless (defined $nl->get_value('tracer_cnst_type')) {
	add_default($nl, 'tracer_cnst_type',    'ver'=>'fixed_ch4');
	add_default($nl, 'tracer_cnst_cycle_yr','ver'=>'fixed_ch4');
    }

    my $val = "'prsd_ch4:CH4'";
    add_default($nl, 'prescribed_ghg_specifier', 'val'=>$val);
    my @files = ( 'prescribed_ghg_datapath','prescribed_ghg_file', 'prescribed_ghg_filelist');
    foreach my $file (@files) {
	add_default($nl, $file, 'ver'=>'fixed_ch4');
    }
    unless (defined $nl->get_value('prescribed_ghg_type')) {
	add_default($nl, 'prescribed_ghg_type',    'ver'=>'fixed_ch4');
	add_default($nl, 'prescribed_ghg_cycle_yr','ver'=>'fixed_ch4');
    }
}

# MAM settings

if ($chem eq 'super_fast_llnl_mam3') {
    #
    add_default($nl, 'use_cam_sulfchem', 'val'=>'.false.');

    # Surface emission datasets:
    my $val;
    my %species;
    %species = (
	        'CH2O   -> ' => 'ch2o_emis_file',
	        'CO     -> ' => 'co_emis_file',
	        'DMS    -> ' => 'dms_emis_file',
		'NO     -> ' => 'nox_emis_file',
		'SO2    -> ' => 'so2_emis_file',
                'SOAG   -> ' => 'soag_emis_file',
                'bc_a1  -> ' => 'bc_a1_emis_file',
                'pom_a1 -> ' => 'pom_a1_emis_file',
                'so4_a1 -> ' => 'so4_a1_emis_file',
                'so4_a2 -> ' => 'so4_a2_emis_file',
                'num_a1 -> ' => 'num_a1_emis_file',
                'num_a2 -> ' => 'num_a2_emis_file',
		);
    if ( ! $clm_vocsrc ) {
	%species = (%species , 
		   'ISOP     -> ' => 'isop_emis_file',  );
    }
    add_default($nl, 'srf_emis_type',  'val'=>'CYCLICAL');
    my $first = 1; my $pre = "";
    foreach my $id (sort keys %species) {
        my $rel_filepath = get_default_value($species{$id});
	my $abs_filepath = set_abs_filepath($rel_filepath, $inputdata_rootdir);
	$val .= $pre . quote_string($id . $abs_filepath);
	if ($first) {
	    $pre = ",";
	    $first = 0;
	}
    }
    add_default($nl, 'srf_emis_specifier', 'val'=>$val);

    # vertical emissions... 
    %species = ();
    %species = (
                'SO2    -> ' => 'so2_vrt_emis_file',
                'so4_a1 -> ' => 'so4_a1_vrt_emis_file',
                'so4_a2 -> ' => 'so4_a2_vrt_emis_file',
                'pom_a1 -> ' => 'pom_a1_vrt_emis_file',
                'bc_a1  -> ' => 'bc_a1_vrt_emis_file',
                'num_a1 -> ' => 'num_a1_vrt_emis_file',
                'num_a2 -> ' => 'num_a2_vrt_emis_file',
		);
    add_default($nl, 'ext_frc_type', 'val'=>"'CYCLICAL'");

    $first = 1; $pre = ""; $val = "";
    foreach my $id (sort keys %species) {
        my $rel_filepath = get_default_value($species{$id});
        my $abs_filepath = set_abs_filepath($rel_filepath, $inputdata_rootdir);
        $val .= $pre . quote_string($id . $abs_filepath);
        if ($first) {
            $pre = ",";
            $first = 0;
        }
    }
    add_default($nl, 'ext_frc_specifier', 'val'=>$val);

    # Species with fixed stratosphere values
    $val = "'NO','NO2','HNO3','CO'";
    add_default($nl, 'fstrat_list', 'val'=>$val);

    # Datasets
    my @files = ('airpl_emis_file', 'soil_erod', 'flbc_file', 'fstrat_file',
                 'sulf_file', 'tuv_xsect_file', 'o2_xsect_file', 'xs_long_file', 'rsf_file', 
                 'solar_parms_file', 'exo_coldens_file', 'sad_file', 
                 'linoz_data_path' , 'linoz_data_file', 'chlorine_loading_file' );
    foreach my $file (@files) {
	add_default($nl, $file);
    }
    unless (defined $nl->get_value('sad_type')) {
	add_default($nl, 'sad_type',  'val'=>'CYCLICAL');
	add_default($nl, 'sad_cycle_yr',  'val'=>'1990');
    }

    unless (defined $nl->get_value('chlorine_loading_type')) {
	add_default($nl, 'chlorine_loading_type',  'val'=>'FIXED');
	add_default($nl, 'chlorine_loading_fixed_ymd',  'val'=>'19900101');
    }
    add_default($nl, 'xactive_prates', 'val'=>'.false.');

    # Prescribed methane
    my $val = "'CH4'";
    add_default($nl, 'tracer_cnst_specifier', 'val'=>$val);
    my @files = ( 'tracer_cnst_datapath','tracer_cnst_file', 'tracer_cnst_filelist');
    foreach my $file (@files) {
	add_default($nl, $file, 'ver'=>'fixed_ch4');
    }
    unless (defined $nl->get_value('tracer_cnst_type')) {
	add_default($nl, 'tracer_cnst_type',    'ver'=>'fixed_ox');
	add_default($nl, 'tracer_cnst_cycle_yr','ver'=>'fixed_ox');
    }

    my $val = "'prsd_ch4:CH4'";
    add_default($nl, 'prescribed_ghg_specifier', 'val'=>$val);
    my @files = ( 'prescribed_ghg_datapath','prescribed_ghg_file', 'prescribed_ghg_filelist');
    foreach my $file (@files) {
	add_default($nl, $file, 'ver'=>'fixed_ch4');
    }
    unless (defined $nl->get_value('prescribed_ghg_type')) {
	add_default($nl, 'prescribed_ghg_type',    'ver'=>'fixed_ch4');
	add_default($nl, 'prescribed_ghg_cycle_yr','ver'=>'fixed_ch4');
    }
}

if ($chem eq 'trop_mam3') {

    #
    add_default($nl, 'use_cam_sulfchem', 'val'=>'.false.');

    my %species;

    # Surface emission datasets:
    %species = ();
    %species = ('DMS       -> ' => 'dms_emis_file',
                'SO2       -> ' => 'so2_emis_file',
                'SOAG      -> ' => 'soag_emis_file',
                'bc_a1     -> ' => 'bc_a1_emis_file',
                'pom_a1    -> ' => 'pom_a1_emis_file',
                'so4_a1    -> ' => 'so4_a1_emis_file', 
                'so4_a2    -> ' => 'so4_a2_emis_file',
                'num_a1    -> ' => 'num_a1_emis_file', 
                'num_a2    -> ' => 'num_a2_emis_file',
	);
    my $first = 1;
    my $pre = "";
    my $val = "";
    foreach my $id (sort keys %species) {
        my $rel_filepath = get_default_value($species{$id});
        my $abs_filepath = set_abs_filepath($rel_filepath, $inputdata_rootdir);
        $val .= $pre . quote_string($id . $abs_filepath);
        if ($first) {
            $pre = ",";
            $first = 0;
        }
    }
    add_default($nl, 'srf_emis_specifier', 'val'=>$val);
    add_default($nl, 'srf_emis_type',      'val'=>'CYCLICAL');

    # Vertical emission datasets:
    %species = ();
    %species = ('SO2         -> ' => 'so2_ext_file',
                'so4_a1      -> ' => 'so4_a1_ext_file', 
                'so4_a2      -> ' => 'so4_a2_ext_file',
                'pom_a1      -> ' => 'pom_a1_ext_file', 
                'bc_a1       -> ' => 'bc_a1_ext_file', 
                'num_a1      -> ' => 'num_a1_ext_file', 
                'num_a2      -> ' => 'num_a2_ext_file', );
    $first = 1;
    $pre = "";
    $val = "";
    foreach my $id (sort keys %species) {
        my $rel_filepath = get_default_value($species{$id});
        my $abs_filepath = set_abs_filepath($rel_filepath, $inputdata_rootdir);
        $val .= $pre . quote_string($id . $abs_filepath);
        if ($first) {
            $pre = ",";
            $first = 0;
        }
    }
    add_default($nl, 'ext_frc_specifier', 'val'=>$val);
    add_default($nl, 'ext_frc_type',      'val'=>"'CYCLICAL'");

    # Prescribed species
    add_default($nl, 'tracer_cnst_specifier', 'val'=>"'O3','OH','NO3','HO2'");
    unless (defined $nl->get_value('tracer_cnst_type')) {
	add_default($nl, 'tracer_cnst_type',    'ver'=>'fixed_ox');
	add_default($nl, 'tracer_cnst_cycle_yr','ver'=>'fixed_ox');
    }

    my @files = ('tracer_cnst_datapath', 'tracer_cnst_file', 'tracer_cnst_filelist');
    foreach my $file (@files) {
        add_default($nl, $file, 'ver'=>'fixed_ox');
    }

    add_default($nl, 'fstrat_list', 'val'=>"' '");
    add_default($nl, 'flbc_list', 'val'=>"' '");
    add_default($nl, 'xactive_prates', 'val'=>'.false.');

    # Datasets
    my @files = ('soil_erod',
                 'xs_long_file', 'rsf_file', 'exo_coldens_file' );
    foreach my $file (@files) {
        add_default($nl, $file);
    }
}

if ($chem eq 'trop_mam7') {

    #
    add_default($nl, 'use_cam_sulfchem', 'val'=>'.false.');

    my %species;

    # Surface emission datasets:
    add_default($nl, 'srf_emis_type', 'val'=>'CYCLICAL');
    %species = ();
    %species = ('bc_a3    -> ' => 'cb1_emis_file',
                'DMS      -> ' => 'dms_emis_file',
                'NH3      -> ' => 'nh3_emis_file',
                'pom_a3   -> ' => 'oc1_emis_file',
                'SOAG     -> ' => 'SOAG_emis_file',
                'SO2      -> ' => 'so2_emis_file',
                'so4_a1   -> ' => 'so4_emis_file',
                'so4_a2   -> ' => 'so4_emis_file', );
    my $first = 1;
    my $pre = "";
    my $val = "";
    foreach my $id (sort keys %species) {
        my $rel_filepath = get_default_value($species{$id});
        my $abs_filepath = set_abs_filepath($rel_filepath, $inputdata_rootdir);
        $val .= $pre . quote_string($id . $abs_filepath);
        if ($first) {
            $pre = ",";
            $first = 0;
        }
    }
    add_default($nl, 'srf_emis_specifier', 'val'=>$val);
    add_default($nl, 'srf_emis_type',      'val'=>"'CYCLICAL'");

    # Vertical emission datasets:
    %species = ();
    %species = ('SO2         -> ' => 'so2_vrt_emis_file',
                'so4_a1      -> ' => 'so4_vrt_emis_file',
                'so4_a2      -> ' => 'so4_vrt_emis_file',
                'num_a1      -> ' => 'so4_vrt_emis_file',
                'num_a2      -> ' => 'so4_vrt_emis_file', );
    $first = 1;
    $pre = "";
    $val = "";
    foreach my $id (sort keys %species) {
        my $rel_filepath = get_default_value($species{$id});
        my $abs_filepath = set_abs_filepath($rel_filepath, $inputdata_rootdir);
        $val .= $pre . quote_string($id . $abs_filepath);
        if ($first) {
            $pre = ",";
            $first = 0;
        }
    }
    add_default($nl, 'ext_frc_specifier', 'val'=>$val);
    add_default($nl, 'ext_frc_type',      'val'=>"'CYCLICAL'");

    # Prescribed species
    add_default($nl, 'tracer_cnst_specifier', 'val'=>"'O3','OH','NO3','HO2'");
    unless (defined $nl->get_value('tracer_cnst_type')) {
	add_default($nl, 'tracer_cnst_type',    'ver'=>'fixed_ox');
	add_default($nl, 'tracer_cnst_cycle_yr','ver'=>'fixed_ox');
    }


    my @files = ('tracer_cnst_datapath', 'tracer_cnst_file', 'tracer_cnst_filelist');
    foreach my $file (@files) {
        add_default($nl, $file, 'ver'=>'fixed_ox');
    }

    add_default($nl, 'fstrat_list', 'val'=>"' '");
    add_default($nl, 'flbc_list', 'val'=>"' '");
    add_default($nl, 'xactive_prates', 'val'=>'.false.');

    # Datasets
    my @files = ('soil_erod',
                 'xs_long_file', 'rsf_file', 'exo_coldens_file' );
    foreach my $file (@files) {
        add_default($nl, $file);
    }
}

# WACCM_MOZART options.
if ($chem =~ /waccm_mozart/) {

    # Species with fixed lower boundary
    my $val = "'CCL4', 'CF2CLBR', 'CF3BR', 'CFC11', 'CFC113', 'CFC12', 'CH3BR', 'CH3CCL3', 'CH3CL', 'CH4', 'CO2', 'H2', 'HCFC22','N2O'";
    add_default($nl, 'flbc_list', 'val'=>$val);
    unless (defined $nl->get_value('flbc_type')) {
	add_default($nl, 'flbc_type',  'val'=>'CYCLICAL');
	add_default($nl, 'flbc_cycle_yr',  'val'=>'1990');
    }
    unless (defined $nl->get_value('sad_type')) {
	add_default($nl, 'sad_type',  'val'=>'CYCLICAL');
	add_default($nl, 'sad_cycle_yr',  'val'=>'1990');
    }

    # Datasets
    my @files = ('flbc_file', 'efield_lflux_file', 'efield_hflux_file', 'efield_wei96_file',
		 'euvacdat_file', 'photon_file', 'electron_file',
                 'euvac_file', 'solar_parms_file', 'sad_file', 'sulf_file', 
                 'depvel_lnd_file', 'clim_soilw_file', 'season_wes_file',
                 'xs_coef_file', 'xs_short_file','xs_long_file', 'rsf_file', 
                 'tgcm_ubc_file', 'snoe_ubc_file' );

    foreach my $file (@files) {
	add_default($nl, $file);
    }

    # aircraft (vertical) emissions... 
    my %species = ();
    if ($ipcc_aircraft_emis) {
        %species = ('NO2 -> ' => 'no2_aircraft_emis_file' );
    } else {
        %species = ('CO      -> ' => 'co_aircraft_emis',
	   	    'NO      -> ' => 'no_aircraft_emis' );
    }
    my $first = 1; my $pre = ""; my $val = "";
    foreach my $id (sort keys %species) {
        my $rel_filepath = get_default_value($species{$id});
        my $abs_filepath = set_abs_filepath($rel_filepath, $inputdata_rootdir);
        $val .= $pre . quote_string($id . $abs_filepath);
        if ($first) {
            $pre = ",";
            $first = 0;
        }
    }
    add_default($nl, 'ext_frc_specifier', 'val'=>$val);
    unless (defined $nl->get_value('ext_frc_type')) {
	add_default($nl, 'ext_frc_type',      'val'=>'CYCLICAL');
	add_default($nl, 'ext_frc_cycle_yr',  'val'=>'1999');
    }

    # Surface emission datasets:
    my $val;
    my %species = ('CO   -> ' => 'co_emis_file',
		   'CH2O -> ' => 'ch2o_emis_file',
		   'NO   -> ' => 'nox_emis_file' );

    my $first = 1; my $pre = "";
    foreach my $id (sort keys %species) {
	my $rel_filepath = get_default_value($species{$id});
	my $abs_filepath = set_abs_filepath($rel_filepath, $inputdata_rootdir);
	$val .= $pre . quote_string($id . $abs_filepath);
	if ($first) {
	    $pre = ",";
	    $first = 0;
	}
    }
    add_default($nl, 'srf_emis_specifier', 'val'=>$val);
    unless (defined $nl->get_value('srf_emis_type')) {
	add_default($nl, 'srf_emis_type',  'val'=>'CYCLICAL');
	add_default($nl, 'srf_emis_cycle_yr',  'val'=>'1997');
    }

    add_default($nl, 'xactive_prates', 'val'=>'.false.');

    add_default($nl, 'do_circulation_diags', 'val'=>'.true.');   
}

my $lbc_file = $nl->get_value('flbc_file');

if ( ! defined  $lbc_file ) {

    # It's assumed that the LBC file used in WACCM/CAM-Chem contains the 
    # necessary GHGs

    # Greenhouse gas surface values (default to 2000 values)

    my $scenario_ghg = $nl->get_value('scenario_ghg');

    if ((defined $scenario_ghg) and $scenario_ghg =~ /RAMPED/) {
        # read values from dataset
        add_default($nl, 'bndtvghg');
    }
    else {
        # Fixed values for all GHGs.  The co2vmr value may just be the
        # starting value if scenario_ghg == RAMP_CO2_ONLY
	add_default($nl, 'co2vmr');
	add_default($nl, 'ch4vmr');
	add_default($nl, 'n2ovmr');
	add_default($nl, 'f11vmr');
	add_default($nl, 'f12vmr');
    }

}

# Dry Deposition -- The responsibility for dry deposition is shared between CAM and CLM.
# The namelist is read by a driver module, seq_drydep_mod, and the information is shared
# between CAM and CLM by use association of that module.
if ( $chem ) {
    add_default($nl, 'drydep_method');
}

# Physics options

# Add the name of the physics package based on the info in configure.  If the user tries
# to explicitly specify this namelist variable issue error.
my $cam_physpkg = $nl->get_value('cam_physpkg');
if (defined $cam_physpkg) {
    die "CAM Namelist ERROR: User may not specify the value of cam_physpkg.\n".
	"This variable is set by build-namelist based on information\n".
	"from the configure cache file.\n";
}
$cam_physpkg = "'" . "$phys" . "'";  # add quotes to this string value
$nl->set_variable_value('phys_ctl_nl', 'cam_physpkg', $cam_physpkg);

# Chemistry options

# Add the name of the chemistry package based on the info in configure.  If the user tries
# to explicitly specify this namelist variable issue error.
my $cam_chempkg = $nl->get_value('cam_chempkg');
if (defined $cam_chempkg) {
    die "CAM Namelist ERROR: User may not specify the value of cam_chempkg.\n".
       "This variable is set by build-namelist based on information\n".
       "from the configure cache file.\n";
}
if ($chem eq 'waccm_mozart_v1') {
    $cam_chempkg =  "'" . 'waccm_mozart'. "'";
} else {
    $cam_chempkg = "'" . "$chem" . "'";  # add quotes to this string value
}

$nl->set_variable_value('phys_ctl_nl', 'cam_chempkg', $cam_chempkg);

# Deep convection scheme
add_default($nl, 'deep_scheme');

# Eddy scheme (vertical diffusion)
add_default($nl, 'eddy_scheme');

# Defaults for shallow convection scheme and surface flux averaging both depend
# on the value of the eddy scheme.
my $eddy_scheme = $nl->get_value('eddy_scheme');
# Need to strip the quotes to use as a value for attribute matching.
$eddy_scheme =~ s/['"]//g;                                           #"'
add_default($nl, 'shallow_scheme', 'eddy_scheme'=>$eddy_scheme);
add_default($nl, 'srf_flux_avg', 'eddy_scheme'=>$eddy_scheme);

# Microphysics scheme
add_default($nl, 'microp_scheme');
add_default($nl, 'history_microphysics');

# Turbulent Mountain Stress
add_default($nl, 'do_tms');
my $do_tms = $nl->get_value('do_tms');
# Check to be sure that do_tms has been set to true if the user sets a customized
# value of tms_orocnst or tms_z0fac.
if (defined $nl->get_value('tms_orocnst') or defined $nl->get_value('tms_z0fac')) {
    # a tms parameter has been set in the namelist.  Make sure do_tms is true.
    if ( $do_tms =~ /$FALSE/io ) {
	die "CAM Namelist ERROR: If tms_oroconst or tms_z0fac is set do_tms must be .true..\n".
	    "Either remove these from the namelist or add do_tms=.true.\n";
    }
}
if ( $do_tms =~ /$TRUE/io ) {
    add_default($nl, 'tms_orocnst');
    add_default($nl, 'tms_z0fac');
}

# Implicit Turbulent Surface Stress
add_default($nl, 'do_iss');

# Use Convective water in radiation
add_default($nl, 'conv_water_in_rad');

# Cloud fraction
add_default($nl, 'cldfrc_freeze_dry');
add_default($nl, 'cldfrc_ice');
add_default($nl, 'cldfrc_rhminl');
add_default($nl, 'cldfrc_rhminh');
add_default($nl, 'cldfrc_sh1');
add_default($nl, 'cldfrc_sh2');
add_default($nl, 'cldfrc_dp1');
add_default($nl, 'cldfrc_dp2');
add_default($nl, 'cldfrc_premit');

# condensate to rain autoconversion coefficients
add_default($nl, 'zmconv_c0_lnd');
add_default($nl, 'zmconv_c0_ocn');
add_default($nl, 'zmconv_ke');

# moist convection rainwater coefficients
add_default($nl, 'hkconv_cmftau');
add_default($nl, 'hkconv_c0');
if ($phys eq 'cam5') {
  add_default($nl, 'uwshcu_rpen');
}

# cldwat ice coefficients
add_default($nl, 'cldwat_icritc');
add_default($nl, 'cldwat_icritw');
add_default($nl, 'cldwat_conke');
add_default($nl, 'cldwat_r3lcrit');

# Cloud sedimentation
add_default($nl, 'cldsed_ice_stokes_fac');

# Dust emissions tuning factor
# If dust is prognostic ==> supply the tuning factor
if ($chem eq 'trop_mam3' or $chem eq 'trop_mam7' or $chem eq 'super_fast_llnl_mam3' or  
    $chem eq 'trop_bam' or $chem eq 'trop_mozart' or $chem eq 'trop_strat_bam_v1') {
    # check whether turbulent mountain stress parameterization is on
    if ($nl->get_value('do_tms') =~ /$TRUE/io) {
	add_default($nl, 'dust_emis_fact', 'tms'=>'1');
    }
    else {
	add_default($nl, 'dust_emis_fact');
    }
}

# Gravity wave drag settings
if ($waccm_phys) {
    add_default($nl, 'gw_drag_file');
} else {
    add_default($nl, 'fcrit2');
}

# FV dycore
if ($cfg->get('dyn') eq 'fv') {
    add_default($nl, 'fft_flt');
    add_default($nl, 'div24del2flag');
    add_default($nl, 'del2coef');
    add_default($nl, 'filtcw');
    add_default($nl, 'nspltvrm');

    # If the -ntasks argument has been set, then use it to set the default
    # FV decomposition unless the user has already set it.
    my $npr_yz = $nl->get_value('npr_yz');
    if (defined $opts{'ntasks'} and ! defined $npr_yz ) {
	$npr_yz = fv_decomp_set();
	add_default($nl, 'npr_yz', 'val'=>$npr_yz);
    }
}

# EUL dycore
if ($cfg->get('dyn') eq 'eul') {
    add_default($nl, 'eul_nsplit');
}

# Single column model
if ($cfg->get('scam')) {
    add_default($nl, 'iopfile');
}

# CAM generates IOP file for SCAM
if ($cfg->get('camiop')) {
    add_default($nl, 'inithist', 'val'=>'CAMIOP');
    add_default($nl, 'ndens', 'val'=>'1,1');
    add_default($nl, 'mfilt', 'val'=>'1,10');
    add_default($nl, 'nhtfrq', 'val'=>'0,1');
}

# HOMME dycore
# ============

if ($cfg->get('dyn') =~ /homme/) {

    ####################################################
    # namelist group: ctl_nl and filter_nl (for HOMME) #
    ####################################################

    my $hgrid = $cfg->get('hgrid');
    if ($hgrid =~ /ne(.*)np/) {
	add_default($nl, 'homme_ne', 'val'=>$1);
    }
    else {
	die "$ProgName - ERROR: Horizontal grid name can NOT be correctly parsed: hgrid = $hgrid. \n";
    }

    my @vars = qw(homme_nsplit hypervis_order phys_loadbalance
                  statefreq homme_partmethod homme_topology homme_ftype
                  integration nu nu_q nu_top energy_fixer filter_freq phys_tscale
		  interpolate_analysis interp_nlat interp_nlon 
                  interp_type interp_gridtype);

    foreach my $var (@vars) {
	add_default($nl, $var);
    }
}

#-----------------------------------------------------------------------------------------------
# Add defaults for the CAM specific surface models DOM and CSIM4.

# Which ocean model?
my $ocn_comp = $cfg->get('ocn');
my $ocn_group = '';
if ($ocn_comp eq 'dom') {
    $ocn_group = 'dom_inparm';
}
if ($ocn_comp eq 'docn') {
    $ocn_group = 'docn_nml';
}

# Which sea ice model?
my $ice_comp = $cfg->get('ice');
my $ice_group = '';
if ($ice_comp eq 'csim4') {
    $ice_group = 'csim_inparm';
}

# The following variables are set by namelist variables in the special camexp namelist
# group.  They are used to ensure consistency between the setting of namelist variables
# in DOM and CSIM4/CICE.
my $bndtvs = '';
my $focndomain = '';

# The %mask variable is used to set namelist defaults for CLM.
my %mask = ();

# Namelist settings for CAMDOM or DOCN or CSIM4
# These namelists aren't needed for ideal, adiabatic, or aqua-planet modes.
if ( ($ocn_comp eq 'dom' or $ocn_comp eq 'docn' or $ice_comp eq 'csim4') and ! $ideal_or_adia_or_aqua) {

    # Determine default values for variables that must be set consistently between these models.

    # sstcyc
    my $sstcyc = '.true.';
    if (defined $nl->get_value('sstcyc')) { $sstcyc = $nl->get_value('sstcyc'); }

    # bndtvs
    add_default($nl, 'bndtvs', 'sim_year'=>$sim_year);
    $bndtvs = $nl->get_value('bndtvs');

    # focndomain
    # For CAM in standalone mode, CAM determines the focndomain file to use, and then specifies
    # that file to DOM and CSIM4/CICE.
    add_default($nl, 'focndomain');
    $focndomain = $nl->get_value('focndomain');

    # Assume that the focndomain filename contains the name of the ocean grid used to construct
    # the lnd/ocn mask.  Recover the ocean grid name using a regexp match, and use it subsequently
    # to determine consistent CLM files.
    if ($focndomain =~ m/_(gx\dv\d)_/) {
        $mask{'mask'} = $1;
    }
    elsif ($focndomain =~ m/_USGS_/) {
        $mask{'mask'} = 'USGS';
    }

    ##############################
    # namelist group: dom_inparm #
    ##############################
    if ($ocn_group eq 'dom_inparm') {
#jt    if ($ocn_group) {
	$nl->set_variable_value($ocn_group, 'sstcyc',     $sstcyc);
	$nl->set_variable_value($ocn_group, 'bndtvs',     $bndtvs);
	$nl->set_variable_value($ocn_group, 'focndomain', $focndomain);
    }

    ###############################
    # namelist group: csim_inparm #
    ###############################
    if ($ice_group) {
	$nl->set_variable_value($ice_group, 'icecyc',     $sstcyc);
	$nl->set_variable_value($ice_group, 'bndtvs',     $bndtvs);
	$nl->set_variable_value($ice_group, 'focndomain', $focndomain);
    }
}

#-----------------------------------------------------------------------------------------------
# Add defaults for the CLM component 
# Only produce a namelist for CLM if running with CAM standalone scripts
# and if the lnd component is active.

my $lnd_pkg = $cfg->get('lnd');

if ($lnd_pkg eq 'clm' and $cam_build and $lnd_active) {

    ##############################
    # namelist group: clm_inparm #
    ##############################

    # Timestep
    # *** N.B. *** this code enforces the constraint that the CLM timestep match the CAM timestep.
    # Note that a Namelist object method is used to add the value of dtime to clm_inparm to avoid
    # the namelist definition checking done by add_default.  That's because dtime is in CAM's definition
    # file as a variable that belongs in cam_inparm and it can't also be in clm_inparm.  This will be
    # resolved when the CLM component is made responsible for producing the clm_inparm namelist.
    my $clmval = $nl->get_value('dtime');
    $nl->set_variable_value('clm_inparm', 'dtime', $clmval);

    # Check that "nrevsn" is set if this is a branch simulation
    if ($nl->get_value('start_type') =~ /branch/  and  !defined $nl->get_value('nrevsn')) {
	die "$ProgName - ERROR: The CLM2 master restart file must be specified for a branch\n".
	    "       run.  Set the namelist variable NREVSN to the absolute\n".
	    "       pathname for this dataset.\n".
	    "       This can be done on the command-line using the -namelist\n".
	    "       option or in an input namelist file that is specified\n".
	    "       using the -infile option.\n";
    }

    add_default($nl, 'fatmlndfrc', %mask);
    add_default($nl, 'fsnowoptics');
    add_default($nl, 'fsnowaging');

    add_default($nl, 'fatmgrid');

    add_default($nl, 'fpftcon');

    # CO2
    # *** N.B. *** This is forced to use the same value that CAM is using.
    my $co2vmr = $nl->get_value('co2vmr');
    if (defined $co2vmr) {
	my $co2_ppmv = sprintf '%e', $co2vmr * 1.e6;
	$nl->set_variable_value('clm_inparm', 'co2_ppmv', $co2_ppmv);
    }

    # All F compsets currently set co2_type='diagnostic'
    add_default($nl, 'co2_type', 'val'=>'diagnostic');

    add_default($nl, 'urban_hac');
    add_default($nl, 'urban_traffic');

    add_default($nl, 'fsurdat', 'sim_year'=>$sim_year_start);

    # Initial conditions
    # The initial date is an attribute in the defaults file which should be matched unless
    # the user explicitly requests to ignore the initial date via the -ignore_ic_date option, 
    # or just ignore the year of the initial date via the -ignore_ic_year option.
    my $ic_date = $nl->get_value('start_ymd');
    if ($opts{'ignore_ic_date'}) {
        add_default($nl, 'finidat', 'nofail'=>1, 'sim_year'=>$sim_year_start, %mask);
    }
    elsif ($opts{'ignore_ic_year'}) {
        add_default($nl, 'finidat', 'ic_md'=>$ic_date, 'nofail'=>1, 'sim_year'=>$sim_year_start, %mask);
    }
    else {
        add_default($nl, 'finidat', 'ic_ymd'=>$ic_date, 'nofail'=>1, 'sim_year'=>$sim_year_start, %mask);
    }

    # If sim_year is input as a range of years, then add the dynamic PFT dataset.
    if ($sim_year =~ /(\d+)-(\d+)/) {
	add_default($nl, 'fpftdyn', 'sim_year'=>$sim_year)
    }
}

#-----------------------------------------------------------------------------------------------
# Add defaults for the CICE component 
# Only produce a namelist for CICE if running with CAM standalone scripts.

if ($ice_comp eq 'cice' and $cam_build and $ice_active) {

    # The CICE component has it's own build-namelist utility.
    my $cice_bldnl = "$cam_root/models/ice/cice/bld/build-namelist";

    # Set up the commandline args

    # CICE config_cache filepath 
    my $cice_config_dir = $cfg->get('cice_config_cache_dir');
    my $cice_config_file = $cfg->get('cice_config_cache_file');
    my $cice_config_filepath = "$cice_config_dir/$cice_config_file";
    if (defined $opts{'config_cice'}) {
	$cice_config_filepath = $opts{'config_cice'};
    }

    # Pass namelist settings to CICE.
    my $cice_nl = "&ice \n";

    if ($ocn_comp eq 'dom' or $ocn_comp eq 'docn') {

	# CICE namelist variables that need to be set consistently with CAM-DOM.
	$cice_nl .= 
	    "grid_file=$focndomain \n" .
	    "kmt_file=$focndomain \n" .
	    "stream_domfilename=$bndtvs \n" .
	    "stream_fldfilename=$bndtvs \n" .
	    "stream_fldvarname='ice_cov' \n";

	# Check for variables used to specify the years of a multi-year SST dataset.
	# If not set by user, default to 0 which is the year used in the climatology files.
	my $stream_year_first = $nl->get_value('stream_year_first');
	unless (defined $stream_year_first) { $stream_year_first = 0; }
	$cice_nl .= "stream_year_first=$stream_year_first \n";

	# Set model_year_align equal to stream_year_first.  The flexibility enabled 
	# by model_year_align is not available in CAM-DOM.
	$cice_nl .= "model_year_align=$stream_year_first \n";

	my $stream_year_last = $nl->get_value('stream_year_last');
	unless (defined $stream_year_last) { $stream_year_last = 0; }
	$cice_nl .= "stream_year_last=$stream_year_last \n";

    }

    # If the user has specified variables to send to the CICE build-namelist
    # via the -cice_nl argument then append them here.
    if (defined $opts{'cice_nl'}) {
	my $usr_cice_nl = $opts{'cice_nl'};

	# strip off the leading group name and trailing terminator
	$usr_cice_nl =~ s/^\s*&\w+//;  # group name with optional leading whitespace
	$usr_cice_nl =~ s/\/\s*$//;    # slash terminator with optional trailing whitespace

	$cice_nl .= $usr_cice_nl;
    }

    # For CAM5 need to send the variable "cam5=.true." to get correct defaults for several
    # cice parameters.
    if ($phys eq 'cam5') {
	$cice_nl .= "cam5=.true. \n";
    }

    # Terminate the namelist
    $cice_nl .= " /";

    # Commandline arguments for CICE build-namelist
    my @nl_args = ('-csmdata',       $inputdata_rootdir,
                  '-config',         $cice_config_filepath,
                   '-dir',           $opts{'dir'},
                  '-namelist',       $cice_nl,
	);

    my @command = ($cice_bldnl, @nl_args);
    system(@command) == 0  or die "system @command failed: $? \n";

    my $outfile = "$opts{'dir'}/ice_in";
    if ($print>=1) { print "Writing CICE namelist to $outfile $eol"; }
}

#-----------------------------------------------------------------------------------------------
# Create docn namelists and stream txt file

if ($ocn_comp eq 'docn' and $cam_build) {
    my  $fh = new IO::File;

    my $stream_year_first = $nl->get_value('stream_year_first');
    unless (defined $stream_year_first) { $stream_year_first = 0; }
    my $stream_year_last = $nl->get_value('stream_year_last');
    unless (defined $stream_year_last) { $stream_year_last = 0; }

    my $temp = $focndomain;
    $temp  =~ s/['"]//g;
    my $focndomain_file = basename($temp);
    my $focndomain_path = dirname($temp);

    my $temp = $bndtvs;
    $temp  =~ s/['"]//g;
    my $sstdata_file=  basename($temp);
    my $sstdata_path = dirname($temp);

    my $create_stream = 1;
    if ($aqua_mode) {$create_stream = 0};


    # add docn nml params
    # --------------

    add_default($nl, 'ocn_in', 'val'=>'docn_ocn_in');
    add_default($nl, 'decomp', 'val'=>'1d');


    # Create docn_ocn_in
    # --------------
    my $outfile = "$opts{'dir'}/docn_ocn_in";
    $fh->open(">$outfile") or die "** can't open file: $outfile\n";

    print $fh  <<"EOF";
 &shr_strdata_nml
   dataMode   = 'SSTDATA'
   domainFile = $focndomain
   streams    = 'docn.stream.txt $stream_year_first $stream_year_first $stream_year_last '
   vectors    = 'null'
 /
EOF

    $fh->close;
    if ($print>=1) { print "Writing DOCN namelist to $outfile $eol"; }

    if ($create_stream) {

        # Create docn.stream.txt - ONLY for non aqua_planet mode
        # --------------
        my $outfile = "$opts{'dir'}/docn.stream.txt";
        $fh->open(">$outfile") or die "** can't open file: $outfile\n";

        print $fh  <<"EOF";
<streamstemplate>
<stream>
      <comment>
         Stream description file for generic SST/ifrac data on the CAM grid
      </comment> 
      <dataSource>
         DATASET
      </dataSource>
      <domainInfo> 
         <variableNames>
            time time
            xc   lon
            yc   lat
            area area
            mask mask
         </variableNames>
         <filePath>
            $focndomain_path
         </filePath>
         <fileNames>
            $focndomain_file
         </fileNames>
      </domainInfo>
      <fieldInfo>
         <variableNames>
            SST_cpl    t
         </variableNames>
         <filePath>
            $sstdata_path
         </filePath>
         <fileNames>
            $sstdata_file
         </fileNames>
      </fieldInfo>
</stream>
</streamstemplate>

EOF

        $fh->close;
        if ($print>=1) { print "Writing DOCN stream file to $outfile $eol"; }
    }

}

#-----------------------------------------------------------------------------------------------
# Rename component logfiles.
#
# This requires special handling because the logfiles are coordinated using 
# share code.  Each component is using shr_file_mod::shr_file_setIO to attach
# a unique Fortran unit to a named file.  shr_file_setIO reads the namelist
# group /modelio/ and hence each component is using the same variable names.
# To work around this component specific names have been added to the special
# namelist group /camexp/.  Check for those variables and write the component
# specific versions of /modelio/ to files whose names are hardwired in the top
# level component interface, e.g., atm_comp_mct, lnd_comp_mct, and seq_ccsm_drv.

my @comps = qw(atm cpl lnd);
foreach my $comp (@comps) {

    my $logfile    = "${comp}_logfile";
    my $logfiledir = "${comp}_logfile_diro";
    my $outfile    = "${comp}_modelio.nml";

    # check to see if user requests renaming this component logfile
    my $logfile_val = $nl->get_variable_value('camexp', $logfile);
    if (defined $logfile_val) {

	# create namelist object for /modelio/
	my $modelio = Build::Namelist->new("&modelio logfile=$logfile_val /");

	# check whether a directory has been specified... if so add to namelist
	my $logfiledir_val = $nl->get_variable_value('camexp', $logfiledir);
	if (defined $logfiledir_val) {
	    $modelio->set_variable_value('modelio', 'diro', $logfiledir_val);
	}	

	# write the namelist to the component specific file
	$modelio->write($outfile);
	if ($print>=1) { print "Writing modelio namelist to $outfile $eol"; }
    }
}

#-----------------------------------------------------------------------------------------------
# Write output files

# Get array of group names that have been defined in the namelist.
my @nl_groups = $nl->get_group_names();

# Create a hash with the group names as keys.  This makes it easy to remove
# names from the list.
my %nl_group = ();
foreach my $name (@nl_groups) { $nl_group{$name} = ''; }

# Write fixed sets of namelist groups for the components other than CAM.
# Remove the group names from %nl_group as they are written.  

# CLM
my @comp_groups = qw(clm_inparm);
my $outfile = "$opts{'dir'}/lnd_in";
if ($cam_build and $lnd_active) {
    $nl->write($outfile, 'groups'=>\@comp_groups);
    if ($print>=1) { print "Writing CLM namelist to $outfile $eol"; }

}
delete @nl_group{@comp_groups};

# Driver
@comp_groups = qw(ccsm_pes seq_infodata_inparm seq_timemgr_inparm prof_inparm pio_inparm);
$outfile = "$opts{'dir'}/drv_in";
if ($cam_build) {
    $nl->write($outfile, 'groups'=>\@comp_groups);
    if ($print>=1) { print "Writing driver namelist to $outfile $eol"; }
}
delete @nl_group{@comp_groups};

# Dry deposition namelist
@comp_groups = qw(drydep_inparm);
$outfile = "$opts{'dir'}/drv_flds_in";
$nl->write($outfile, 'groups'=>\@comp_groups);
if ($print>=1) { print "Writing dry deposition namelist to $outfile $eol"; }
delete @nl_group{@comp_groups};

# Ocean component (CAMDOM)
if ($ocn_group eq 'dom_inparm') {
    $outfile = "$opts{'dir'}/ocn_in";
}
else {
    $outfile = "$opts{'dir'}/docn_in";
}
if ($ocn_group) {
    @comp_groups = ($ocn_group);
    $nl->write($outfile, 'groups'=>\@comp_groups);
    delete @nl_group{@comp_groups};
    if ($print>=1) { print "Writing ocean component namelist to $outfile $eol"; }
}

# Sea ice component (CSIM4)
if ($ice_group and $ice_active) {
    @comp_groups = ($ice_group);
    $outfile = "$opts{'dir'}/ice_in";
    $nl->write($outfile, 'groups'=>\@comp_groups);
    delete @nl_group{@comp_groups};
    if ($print>=1) { print "Writing sea ice component namelist to $outfile $eol"; }
}

# CAM
#
# Have removed the group names for components not associated with CAM, so write
# the remaining groups to the CAM namelist file.
# *** N.B. *** The assumption that all remaining groups belong to CAM allows adding
#              new groups to CAM's definition file without requiring a code modification
#              here to have that group written to the namelist file.

# First remove the camexp group since it's a special purpose group that's not
# read by CAM
if (defined $nl_group{'camexp'}) { delete $nl_group{'camexp'}; }
@comp_groups = sort keys %nl_group;
$outfile = "$opts{'dir'}/atm_in";
$nl->write($outfile, 'groups'=>\@comp_groups);
if ($print>=1) { print "Writing CAM namelist to $outfile $eol"; }

#-----------------------------------------------------------------------------------------------
# Test that input files exist locally.
if ($opts{'test'}) {
    print "Checking whether input datasets exist locally...$eol";
    check_input_files($nl);
}

# Write the input dataset filepaths to the file specified by $opts{'inputdata'}.
if ($opts{'inputdata'}) {
    check_input_files($nl, $inputdata_rootdir, $opts{'inputdata'});
}

# END OF MAIN SCRIPT
#===============================================================================================

sub add_default {

# Add a value for the specified variable to the specified namelist object.  The variables
# already in the object have the higher precedence, so if the specified variable is already
# defined in the object then don't overwrite it, just return.
#
# This method checks the definition file and adds the variable to the correct
# namelist group.
#
# The value can be provided by using the optional argument key 'val' in the
# calling list.  Otherwise a default value is obtained from the namelist
# defaults object.  If no default value is found this method throws an exception
# unless the 'nofail' option is set true.
#
# Additional optional keyword=>value pairs may be specified.  If the keyword 'val' is
# not present, then any other keyword=>value pairs that are specified will be used to
# match attributes in the defaults file.
#
# Example 1: Specify the default value $val for the namelist variable $var in namelist
#            object $nl:
#
#  add_default($nl, $var, 'val'=>$val)
#
# Example 2: Add a default for variable $var if an appropriate value is found.  Otherwise
#            don't add the variable
#
#  add_default($nl, $var, 'nofail'=>1)
#
#
# ***** N.B. ***** This routine assumes the following variables are in package main::
#  $definition        -- the namelist definition object
#  $inputdata_rootdir -- CCSM inputdata root directory

    my $nl = shift;     # namelist object
    my $var = shift;    # name of namelist variable
    my %opts = @_;      # options

    my $val = undef;

    # Query the definition to find which group the variable belongs to.  Exit if not found.
    my $group = $definition->get_group_name($var);
    unless ($group) {
	my $fname = $definition->get_file_name();
	die "$ProgName - ERROR: variable \"$var\" not found in namelist definition file $fname.\n";
    }

    # check whether the variable has a value in the namelist object -- if so then return
    $val = $nl->get_variable_value($group, $var);
    if (defined $val) {return;}

    # Look for a specified value in the options hash
    if (defined $opts{'val'}) {
	$val = $opts{'val'};
    }
    # or else get a value from namelist defaults object.
    # Note that if the 'val' key isn't in the hash, then just pass anything else
    # in %opts to the get_value method to be used as attributes that are matched
    # when looking for default values.
    else {
	$val = get_default_value($var, \%opts);
    }

    # if no value is found then exit w/ error (unless 'nofail' option set)
    unless (defined $val) {
	unless ($opts{'nofail'}) {
	    print "$ProgName - ERROR: No default value found for $var\n".
		"user defined attributes:\n";
	    foreach my $key (keys(%opts)) {
		if ($key ne 'nofail' and $key ne 'val') {
		    print "key=$key  val=$opts{$key}\n";
		}
	    }
	    die;
	}
	else {
	    return;
	}
    }

    # query the definition to find out if the variable is an input pathname
    my $is_input_pathname = $definition->is_input_pathname($var);

    # The default values for input pathnames are relative.  If the namelist
    # variable is defined to be an absolute pathname, then prepend
    # the CCSM inputdata root directory.
    if ($is_input_pathname eq 'abs') {
	$val = set_abs_filepath($val, $inputdata_rootdir);
    }

    # query the definition to find out if the variable takes a string value.
    # The returned string length will be >0 if $var is a string, and 0 if not.
    my $str_len = $definition->get_str_len($var);

    # If the variable is a string, then add quotes if they're missing
    if ($str_len > 0) {
	$val = quote_string($val);
    }

    # set the value in the namelist
    $nl->set_variable_value($group, $var, $val);
}

#-----------------------------------------------------------------------------------------------

sub get_default_value {

# Return a default value for the requested variable.
# Return undef if no default found.
#
# ***** N.B. ***** This routine assumes the following variables are in package main::
#  $defaults          -- the namelist defaults object
#  $uc_defaults       -- the use case defaults object

    my $var_name    = lc(shift);   # name of namelist variable (case insensitive interface)
    my $usr_att_ref = shift;       # reference to hash containing user supplied attributes

    # Check first in the use case defaults
    if (defined $uc_defaults) {
	my $val = $uc_defaults->get_value($var_name, $usr_att_ref);
	if ($val) {return $val;}
    }

    # Check in the namelist defaults
    return $defaults->get_value($var_name, $usr_att_ref);

}

#-----------------------------------------------------------------------------------------------

sub fv_decomp_set{

# Return a decomposition for the FV dycore, expressed as the value for the namelist
# variable npr_yz.  The value of npr_yz is ny,nz,nx,ny where ny, nz, and nx are
# respecitively the number of y, z, and x subdomains in the multi-2D decomposition.
# The following constraints must be observed: 
# 1) ny*nz = ntasks  (ny*nz can be less than ntasks in the case that the dynamics runs
#                     on a subset of the total tasks.  Assume the user will set the
#                     decomp explicitly if that is what is desired.)
# 2) nx=nz           (this is an empirically determined constraint for efficiency in the
#                     dycore communications)
# 3) ny*3 <= nlat    (these constraints are imposed by the dycore)
#    nz   <= nlev
#
# The strategy for setting the default decomposition is to use the smallest possible
# number of z subdomains.  Start with 1 (which is the 1D decomposition).  If that works
# then done.  If not, increase nz, check that it divides ntasks, if so does resulting
# ny satisfy ny*3 <= nlat, if so then done.  Etc.

    my $ntasks = $opts{'ntasks'};
    my $nlat = $cfg->get('nlat');
    my $nlev = $cfg->get('nlev');
    my ($ny, $nz);

    NZ: for ($nz = 1; $nz <= $nlev; ++$nz) {

	# test that $nz divides $ntasks
	if ($ntasks%$nz == 0) {

	    $ny = $ntasks/$nz;

	    # test that y subdomains contain at least 3 latitudes
	    # if so then done
	    if (3*$ny <= $nlat) {last NZ;}
	}
    }

    # die if algorithm failed
    if ($nz > $nlev  or  $ny*$nz != $ntasks) {
	die "$ProgName - ERROR: fv_decomp_set failed to find a decomposition.\n" .
	    "  npr_yz needs to be set by the user.";
    }

    return "$ny,$nz,$nz,$ny";
}

#-----------------------------------------------------------------------------------------------

sub check_input_files {

# For each variable in the namelist which is an input dataset, check to see if it
# exists locally.
#
# ***** N.B. ***** This routine assumes the following variables are in package main::
#  $definition        -- the namelist definition object

    my $nl                = shift;    # namelist object
    my $inputdata_rootdir = shift;    # if false prints test, else creates inputdata file
    my $outfile           = shift;

    if (defined $inputdata_rootdir) {
	open(OUTFILE, ">$outfile") or die "check_input_files: error opening $outfile: $!";
    }

    # Look through all namelist groups
    my @groups = $nl->get_group_names();
    foreach my $group (@groups) {

	# Look through all variables in each group
	my @vars = $nl->get_variable_names($group);
	foreach my $var (@vars) {

	    # Is the variable an input dataset?
	    my $input_pathname_type = $definition->is_input_pathname($var);

	    # If it is, check whether it exists locally and print status
	    if ($input_pathname_type) {

		# Get pathname of input dataset
		my $pathname = $nl->get_variable_value($group, $var);
		# Need to strip the quotes
		$pathname =~ s/['"]//g;

		if ($input_pathname_type eq 'abs') {
                    if ($inputdata_rootdir) {
                        $pathname = strip_rootdir($pathname, $inputdata_rootdir);
                        print OUTFILE "$var = $pathname\n";
                    }
                    else {
		        if (-e $pathname) {  # use -e rather than -f since the absolute pathname
			                     # might be a directory
			    print "OK -- found $var = $pathname\n";
		        }
		        else {
			    print "NOT FOUND:  $var = $pathname\n";
		        }
                    }
		}
		elsif ($input_pathname_type =~ m/rel:(.+)/o) {
		    # The match provides the namelist variable that contains the
		    # root directory for a relative filename
		    my $rootdir_var = $1;
		    my $rootdir = $nl->get_variable_value($group, $rootdir_var);
		    $rootdir =~ s/['"]//g;          #"'
                    if ($inputdata_rootdir) {
                        $pathname = "$rootdir/$pathname";
                        $pathname = strip_rootdir($pathname, $inputdata_rootdir);
                        print OUTFILE "$var = $pathname\n";
                    }
                    else {
		        if (-f "$rootdir/$pathname") {
			    print "OK -- found $var = $rootdir/$pathname\n";
		        }
		        else {
			    print "NOT FOUND:  $var = $rootdir/$pathname\n";
		        }
                    }
		}
	    }
	}
    }

    # Treat special cases...

    # These namelist variables are arrays of strings.
    my @vars = qw(ext_frc_specifier srf_emis_specifier rad_climate rad_diag_1 rad_diag_2
                  rad_diag_3 rad_diag_4 rad_diag_5 rad_diag_6 rad_diag_7 rad_diag_8
                  rad_diag_9 rad_diag_10);
    foreach my $var (@vars) {

	# Is the variable in the namelist?
	my $value = $nl->get_value($var);
	if (defined $value) {

	    # The current namelist parser stores all values as a single
	    # string, so start by extracting each quoted string.

	    while ($value =~ m/['"]          # opening quote
                               ([^'"]+)      # capture all non-quote characters up to 
                               ['"]          # closing quote
                               (.*)          # capture rest of string including newlines
                              /sxo) {        # the s modifier lets . match newlines
		my $spec = $1;
		$value = $2;

		# Look for values of the form 'name -> filepath'
		# Extract name and filename
		if ($spec =~ m/\s*(\w+)      # name of species preceded by optional whitespace
                               \s*->\s*      # -> separator surrounded by optional whitespace
                               (\S+)         # filename (all characters up to optional whitespace)
                              /xo) {
		    my $name = $1;
		    my $file = $2;
                    if ($inputdata_rootdir) {
                        $file = strip_rootdir($file, $inputdata_rootdir);
                        print OUTFILE "$var for $name = $file\n";
                    }
                    else {
		        if (-f $file) {
			    print "OK -- found $var for $spec\n";
		        }
		        else {
			    print "NOT FOUND:  $var for $spec\n";
		        }
                    }
		}
		# Look for values of the form 'X_name:name2'
		# Extract name and filename
		elsif ($spec =~ m/\s*._(\w+)    # name of species preceded by optional whitespace and X_
                                  :             # : separator
                                  (\S+)         # name2
                                 /xo) {
		    my $name  = $1;
		    my $name2 = $2;

		    # If $name2 starts with a slash, then it is an absolute filepath.
		    # If $name2 starts with a $, then it is an unresolved filepath
		    # (generated when run from CCSM scripts).
		    # Otherwise ignore it.
		    if ($name2 =~ m:^[/\$]:) {

			my $file = $name2;
			if ($inputdata_rootdir) {
			    $file = strip_rootdir($file, $inputdata_rootdir);
			    print OUTFILE "$var for $name = $file\n";
			}
			else {
			    if (-f $file) {
				print "OK -- found $var for $spec\n";
			    }
			    else {
				print "NOT FOUND:  $var for $spec\n";
			    }
			}
		    }

		}
		else {
		    print "Failed to parse \'$spec\'";
		}

	    } # end of while loop
	}
    }

    close OUTFILE if defined $inputdata_rootdir;
    return 0 if defined $inputdata_rootdir;

}

#-----------------------------------------------------------------------------------------------

sub strip_rootdir {

# Strip a root directory from the begining of a filepath.
# Allow for the possibility that the root directory is specified as a shell variable
# to support a CCSM script requirement.

    my ($filepath, $rootdir) = @_;

    # Check whether the rootdir is specified as a shell variable.
    if ($rootdir =~ m/^\$(\w*)/) {

	my $rootname = $1;

	# Strip off the root directory with the following regexp that
	# avoids the problem of $rootdir being interpolated to a scalar variable
	# name...
	#$filepath =~ s:^\$$rootname::;

	# The CCSM scripts are currently set up to expect the shell variable in the
	# output file that contains the list of inputdata files.  So in this case
	# do nothing.

    }
    else {
	# Strip off the rootdir specified as a resolved pathname
	$filepath =~ s:^$rootdir::;
    }
    return $filepath;
}

#-----------------------------------------------------------------------------------------------

sub set_abs_filepath {

# check whether the input filepath is an absolute path, and if it isn't then
# prepend a root directory

    my ($filepath, $rootdir) = @_;

    # strip any leading/trailing whitespace
    $filepath =~ s/^\s+//;
    $filepath =~ s/\s+$//;
    $rootdir  =~ s/^\s+//;
    $rootdir  =~ s/\s+$//;

    # strip any leading/trailing quotes
    $filepath =~ s/^['"]+//;
    $filepath =~ s/["']+$//;
    $rootdir =~ s/^['"]+//;
    $rootdir =~ s/["']+$//;

    my $out = $filepath;
    unless ( $filepath =~ /^\// ) {  # unless $filepath starts with a /
	$out = "$rootdir/$filepath"; # prepend the root directory
    }
    return $out;
}

#-----------------------------------------------------------------------------------------------


sub absolute_path {
#
# Convert a pathname into an absolute pathname, expanding any . or .. characters.
# Assumes pathnames refer to a local filesystem.
# Assumes the directory separator is "/".
#
  my $path = shift;
  my $cwd = getcwd();  # current working directory
  my $abspath;         # resulting absolute pathname

# Strip off any leading or trailing whitespace.  (This pattern won't match if
# there's embedded whitespace.
  $path =~ s!^\s*(\S*)\s*$!$1!;

# Convert relative to absolute path.

  if ($path =~ m!^\.$!) {          # path is "."
      return $cwd;
  } elsif ($path =~ m!^\./!) {     # path starts with "./"
      $path =~ s!^\.!$cwd!;
  } elsif ($path =~ m!^\.\.$!) {   # path is ".."
      $path = "$cwd/..";
  } elsif ($path =~ m!^\.\./!) {   # path starts with "../"
      $path = "$cwd/$path";
  } elsif ($path =~ m!^[^/]!) {    # path starts with non-slash character
      $path = "$cwd/$path";
  }

  my ($dir, @dirs2);
  my @dirs = split "/", $path, -1;   # The -1 prevents split from stripping trailing nulls
                                     # This enables correct processing of the input "/".

  # Remove any "" that are not leading.
  for (my $i=0; $i<=$#dirs; ++$i) {
      if ($i == 0 or $dirs[$i] ne "") {
	  push @dirs2, $dirs[$i];
      }
  }
  @dirs = ();

  # Remove any "."
  foreach $dir (@dirs2) {
      unless ($dir eq ".") {
	  push @dirs, $dir;
      }
  }
  @dirs2 = ();

  # Remove the "subdir/.." parts.
  foreach $dir (@dirs) {
    if ( $dir !~ /\.\./ ) {
        push @dirs2, $dir;
    } else {
        pop @dirs2;   # remove previous dir when current dir is ..
    }
  }
  if ($#dirs2 == 0 and $dirs2[0] eq "") { return "/"; }
  $abspath = join '/', @dirs2;
  return( $abspath );
}

#-------------------------------------------------------------------------------

sub valid_option {

    my ($val, @expect) = @_;
    my ($expect);

    $val =~ s/^\s+//;
    $val =~ s/\s+$//;
    foreach $expect (@expect) {
	if ($val =~ /^$expect$/i) { return $expect; }
    }
    return undef;
}

#-------------------------------------------------------------------------------

sub validate_use_case {

    my $source = shift;   # text string declaring the source of the options being validated
    my $opts   = shift;   # reference to hash that contains the options

    my ($opt, $old, @expect);
    
    # use_case
    $opt = 'use_case';
    if (defined $opts->{$opt}) {

	# create the @expect array by listing the files in $use_case_dir
	# and strip off the ".xml" part of the filename
	@expect = ();
	my @files = glob("$opts->{'use_case_dir'}/*.xml");
	foreach my $file (@files) {
	    $file =~ m{.*/(.*)\.xml};
	    push @expect, $1;
	}

	$old = $opts->{$opt};
	$opts->{$opt} = valid_option($old, @expect)
	    or die "$ProgName - ERROR: invalid value of $opt ($old) specified in $source\n".
                   "expected one of: @expect\n";
    }

}

#-------------------------------------------------------------------------------

sub quote_string {
    my $str = shift;
    $str =~ s/^\s+//;
    $str =~ s/\s+$//;
    unless ($str =~ /^['"]/) {        #"'
        $str = "\'$str\'";
    }
    return $str;
}

#-------------------------------------------------------------------------------

sub version {
# The version is found in CAM's ChangeLog file.
# $cfgdir is set by the configure script to the name of its directory.

    my ($cfgdir) = @_;

    my $logfile = "$cfgdir/../doc/ChangeLog";

    my $fh = IO::File->new($logfile, '<') or die "** $ProgName - ERROR: can't open ChangeLog file: $logfile\n";

    while (my $line = <$fh>) {

	if ($line =~ /^Tag name:\s*(\w+)/ ) {
	    print "$1\n";
	    last;
	}
    }

}

#-------------------------------------------------------------------------------
